mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 14:52:00 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,13 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from freezegun import freeze_time
|
||||
from datetime import timedelta
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import fields
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import Form, tagged
|
||||
from odoo.tools import float_compare, mute_logger, float_round
|
||||
from odoo.tools import float_compare, float_round, mute_logger
|
||||
|
||||
from odoo.addons.sale.tests.common import SaleCommon
|
||||
|
||||
|
|
@ -19,11 +19,18 @@ class TestSalePrices(SaleCommon):
|
|||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls._enable_discounts()
|
||||
cls.discount = 10 # %
|
||||
|
||||
# Needed when run without demo data
|
||||
# s.t. taxes creation doesn't fail
|
||||
cls.env.company.account_fiscal_country_id = cls.env.ref('base.be')
|
||||
belgium = cls.env.ref('base.be')
|
||||
cls.env.company.account_fiscal_country_id = belgium
|
||||
for model in ('account.tax', 'account.tax.group'):
|
||||
cls.env.add_to_compute(
|
||||
cls.env[model]._fields['country_id'],
|
||||
cls.env[model].search([('company_id', '=', cls.env.company.id)]),
|
||||
)
|
||||
|
||||
def _create_discount_pricelist_rule(self, **additional_values):
|
||||
return self.env['product.pricelist.item'].create({
|
||||
|
|
@ -40,7 +47,6 @@ class TestSalePrices(SaleCommon):
|
|||
)
|
||||
product_price = self.product.lst_price
|
||||
product_dozen_price = product_price * 12
|
||||
discount = 1 - self.discount/100
|
||||
|
||||
self.empty_order.order_line = [
|
||||
Command.create({
|
||||
|
|
@ -58,41 +64,32 @@ class TestSalePrices(SaleCommon):
|
|||
Command.create({
|
||||
'product_id': self.product.id,
|
||||
'product_uom_qty': 1.0,
|
||||
'product_uom': self.uom_dozen.id,
|
||||
'product_uom_id': self.uom_dozen.id,
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': self.product.id,
|
||||
'product_uom_qty': 0.4,
|
||||
'product_uom': self.uom_dozen.id,
|
||||
'product_uom_id': self.uom_dozen.id,
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': self.product.id,
|
||||
'product_uom_qty': 0.3,
|
||||
'product_uom': self.uom_dozen.id,
|
||||
'product_uom_id': self.uom_dozen.id,
|
||||
})
|
||||
]
|
||||
|
||||
discounted_lines = self.empty_order.order_line.filtered('pricelist_item_id')
|
||||
self.assertEqual(discounted_lines, self.empty_order.order_line[1:5])
|
||||
self.assertEqual(discounted_lines.pricelist_item_id, pricelist_rule)
|
||||
self.assertTrue(all(not line.discount for line in self.empty_order.order_line))
|
||||
self.assertEqual(
|
||||
discounted_lines.mapped('price_unit'),
|
||||
[
|
||||
product_price*discount,
|
||||
product_price*discount,
|
||||
product_dozen_price*discount,
|
||||
product_dozen_price*discount
|
||||
]
|
||||
)
|
||||
|
||||
self.pricelist.discount_policy = 'without_discount'
|
||||
self.empty_order._recompute_prices()
|
||||
self.assertTrue(all(not line.discount for line in self.empty_order.order_line - discounted_lines))
|
||||
self.assertEqual(
|
||||
discounted_lines.mapped('price_unit'),
|
||||
[product_price, product_price, product_dozen_price, product_dozen_price])
|
||||
self.assertEqual(discounted_lines.mapped('discount'), [self.discount]*len(discounted_lines))
|
||||
|
||||
discounted_lines[0].product_uom_qty = 3.0
|
||||
self.assertFalse(discounted_lines[0].discount)
|
||||
|
||||
def test_pricelist_dates(self):
|
||||
""" Verify the order date is correctly provided to the pricelist API"""
|
||||
today = fields.Datetime.today()
|
||||
|
|
@ -111,11 +108,9 @@ class TestSalePrices(SaleCommon):
|
|||
'product_id': self.product.id,
|
||||
})
|
||||
|
||||
self.assertEqual(order_line.pricelist_item_id, pricelist_rule)
|
||||
self.assertEqual(
|
||||
order_line.price_unit,
|
||||
self.product.lst_price * (1 - self.discount / 100.0))
|
||||
self.assertEqual(order_line.discount, 0.0)
|
||||
self.assertAlmostEqual(order_line.pricelist_item_id, pricelist_rule)
|
||||
self.assertAlmostEqual(order_line.price_unit, self.product.lst_price)
|
||||
self.assertEqual(order_line.discount, 10)
|
||||
|
||||
# Create an order tomorrow, add line today, rule active today doesn't work
|
||||
self.empty_order.date_order = tomorrow
|
||||
|
|
@ -150,8 +145,8 @@ class TestSalePrices(SaleCommon):
|
|||
self.assertEqual(order_line.pricelist_item_id, pricelist_rule)
|
||||
self.assertEqual(
|
||||
order_line.price_unit,
|
||||
self.product.lst_price * (1 - self.discount / 100.0))
|
||||
self.assertEqual(order_line.discount, 0.0)
|
||||
self.product.lst_price)
|
||||
self.assertEqual(order_line.discount, 10)
|
||||
|
||||
self.assertEqual(
|
||||
self.empty_order.amount_untaxed,
|
||||
|
|
@ -206,7 +201,7 @@ class TestSalePrices(SaleCommon):
|
|||
self.empty_order.order_line = [
|
||||
Command.create({
|
||||
'product_id': self.product.id,
|
||||
'product_uom': self.uom_dozen.id,
|
||||
'product_uom_id': self.uom_dozen.id,
|
||||
'product_uom_qty': 2.0,
|
||||
}),
|
||||
]
|
||||
|
|
@ -220,8 +215,8 @@ class TestSalePrices(SaleCommon):
|
|||
with freeze_time('2022-08-19'):
|
||||
self.env['res.currency.rate'].create({
|
||||
'name': fields.Date.today(),
|
||||
'rate': 1.0,
|
||||
'currency_id': self.env.company.currency_id.id,
|
||||
'rate': 2.0,
|
||||
'currency_id': other_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
})
|
||||
order_in_other_currency = self.env['sale.order'].create({
|
||||
|
|
@ -230,12 +225,13 @@ class TestSalePrices(SaleCommon):
|
|||
'order_line': [
|
||||
Command.create({
|
||||
'product_id': self.product.id,
|
||||
'product_uom': self.uom_dozen.id,
|
||||
'product_uom_id': self.uom_dozen.id,
|
||||
'product_uom_qty': 2.0,
|
||||
}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(order_in_other_currency.amount_total, 480.0)
|
||||
# 20.0 (product price) * 24.0 (2 dozens) * 2.0 (price rate USD -> EUR)
|
||||
self.assertEqual(order_in_other_currency.amount_total, 960.0)
|
||||
|
||||
def test_negative_discounts(self):
|
||||
"""aka surcharges"""
|
||||
|
|
@ -250,7 +246,6 @@ class TestSalePrices(SaleCommon):
|
|||
|
||||
# Even when the discount is supposed to be shown
|
||||
# Surcharges shouldn't be shown to the user
|
||||
self.pricelist.discount_policy = 'without_discount'
|
||||
order_line = self.env['sale.order.line'].create({
|
||||
'order_id': self.empty_order.id,
|
||||
'product_id': self.product.id,
|
||||
|
|
@ -264,7 +259,6 @@ class TestSalePrices(SaleCommon):
|
|||
|
||||
base_pricelist = self.env['product.pricelist'].create({
|
||||
'name': 'First pricelist',
|
||||
'discount_policy': 'without_discount',
|
||||
'item_ids': [Command.create({
|
||||
'compute_price': 'percentage',
|
||||
'base': 'list_price',
|
||||
|
|
@ -275,12 +269,11 @@ class TestSalePrices(SaleCommon):
|
|||
})
|
||||
|
||||
self.pricelist.write({
|
||||
'discount_policy': 'without_discount',
|
||||
'item_ids': [Command.create({
|
||||
'compute_price': 'formula',
|
||||
'compute_price': 'percentage',
|
||||
'base': 'pricelist',
|
||||
'base_pricelist_id': base_pricelist.id,
|
||||
'price_discount': 10,
|
||||
'percent_price': 10,
|
||||
'applied_on': '3_global',
|
||||
'name': 'Second discount',
|
||||
})],
|
||||
|
|
@ -297,7 +290,9 @@ class TestSalePrices(SaleCommon):
|
|||
|
||||
self.assertEqual(order_line.pricelist_item_id, self.pricelist.item_ids)
|
||||
self.assertEqual(order_line.price_subtotal, 81, "Second pricelist rule not applied")
|
||||
self.assertEqual(order_line.discount, 19, "Second discount not applied")
|
||||
self.assertEqual(
|
||||
order_line.discount, 19,
|
||||
"Discount not computed correctly based on both pricelists")
|
||||
|
||||
def test_pricelist_with_another_currency(self):
|
||||
""" Test prices are correctly applied with a pricelist with another currency"""
|
||||
|
|
@ -316,17 +311,14 @@ class TestSalePrices(SaleCommon):
|
|||
).unlink()
|
||||
new_uom = self.env['uom.uom'].create({
|
||||
'name': '10 units',
|
||||
'factor_inv': 10,
|
||||
'uom_type': 'bigger',
|
||||
'rounding': 1.0,
|
||||
'category_id': self.uom_unit.category_id.id,
|
||||
'relative_factor': 10,
|
||||
'relative_uom_id': self.uom_unit.id,
|
||||
})
|
||||
|
||||
# This pricelist doesn't show the discount
|
||||
pricelist_eur = self.env['product.pricelist'].create({
|
||||
'name': 'First pricelist',
|
||||
'currency_id': currency_eur.id,
|
||||
'discount_policy': 'with_discount',
|
||||
'item_ids': [Command.create({
|
||||
'compute_price': 'percentage',
|
||||
'base': 'list_price',
|
||||
|
|
@ -347,13 +339,76 @@ class TestSalePrices(SaleCommon):
|
|||
})
|
||||
|
||||
# force compute uom and prices
|
||||
self.assertEqual(order_line.price_unit, 180, "First pricelist rule not applied")
|
||||
order_line.product_uom = new_uom
|
||||
self.assertEqual(order_line.price_unit, 1800, "First pricelist rule not applied")
|
||||
self.assertEqual(order_line.discount, 10, "First pricelist rule not applied")
|
||||
order_line.product_uom_id = new_uom
|
||||
self.assertEqual(order_line.price_total, 1800, "First pricelist rule not applied")
|
||||
|
||||
def test_pricelist_price_recompute_on_quantity_change(self):
|
||||
"""
|
||||
Test price updates correctly when quantity changes with
|
||||
pricelist based on another pricelist.
|
||||
"""
|
||||
self._enable_pricelists()
|
||||
|
||||
pricelist_a = self.env['product.pricelist'].create({
|
||||
'name': "Pricelist A",
|
||||
'item_ids': [
|
||||
Command.create({
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'fixed',
|
||||
'fixed_price': 0.75,
|
||||
'min_quantity': 0,
|
||||
}),
|
||||
Command.create({
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'fixed',
|
||||
'fixed_price': 0.50,
|
||||
'min_quantity': 1000,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
pricelist_b = self.env['product.pricelist'].create({
|
||||
'name': "Pricelist B",
|
||||
'item_ids': [
|
||||
Command.create({
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': -10,
|
||||
'base': 'pricelist',
|
||||
'base_pricelist_id': pricelist_a.id,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
with Form(self.env['sale.order']) as order_form:
|
||||
order_form.partner_id = self.partner
|
||||
order_form.pricelist_id = pricelist_b
|
||||
with order_form.order_line.new() as line_form:
|
||||
line_form.product_id = self.product
|
||||
self.assertAlmostEqual(line_form.price_unit, 0.825)
|
||||
line_form.product_uom_qty = 1000
|
||||
self.assertAlmostEqual(line_form.price_unit, 0.55)
|
||||
|
||||
def test_compute_price_unit_no_currency(self):
|
||||
new_order = self.env['sale.order'].new({
|
||||
'currency_id': False,
|
||||
'pricelist_id': False,
|
||||
'order_line': [Command.create({'product_id': self.product.id})],
|
||||
})
|
||||
new_line = new_order.order_line
|
||||
self.assertEqual(new_line.price_unit, self.product.list_price)
|
||||
|
||||
new_line.price_unit = new_price = self.product.list_price + 0.5
|
||||
new_line.product_uom_qty += 1.0
|
||||
self.assertEqual(new_line.price_unit, new_price, "Manual unit price shouldn't change")
|
||||
|
||||
new_order._recompute_prices()
|
||||
self.assertEqual(new_line.price_unit, self.product.list_price)
|
||||
|
||||
def test_multi_currency_discount(self):
|
||||
"""Verify the currency used for pricelist price & discount computation."""
|
||||
product_1 = self.consumable_product
|
||||
product_1 = self.product
|
||||
product_2 = self.service_product
|
||||
|
||||
# Make sure the company is in USD
|
||||
|
|
@ -371,7 +426,12 @@ class TestSalePrices(SaleCommon):
|
|||
'company_ids': [Command.set([other_company.id])],
|
||||
'name': 'E.T',
|
||||
'login': 'hohoho',
|
||||
'group_ids': (
|
||||
self.env.ref('sales_team.group_sale_salesman') |
|
||||
self.env.ref('product.group_product_manager')
|
||||
),
|
||||
})
|
||||
user_in_other_company.group_ids += self.env.ref("product.group_product_manager")
|
||||
with mute_logger('odoo.models.unlink'):
|
||||
self.env['res.currency.rate'].search([]).unlink()
|
||||
self.env['res.currency.rate'].create({
|
||||
|
|
@ -401,7 +461,7 @@ class TestSalePrices(SaleCommon):
|
|||
|
||||
pricelist = self.env['product.pricelist'].create({
|
||||
'name': 'Test multi-currency',
|
||||
'discount_policy': 'without_discount',
|
||||
'company_id': False,
|
||||
'currency_id': other_curr.id,
|
||||
'item_ids': [
|
||||
Command.create({
|
||||
|
|
@ -428,7 +488,7 @@ class TestSalePrices(SaleCommon):
|
|||
# product_1.currency != so currency
|
||||
# product_2.cost_currency_id = so currency
|
||||
sales_order = product_1_ctxt.with_context(mail_notrack=True, mail_create_nolog=True).env['sale.order'].create({
|
||||
'partner_id': self.env.user.partner_id.id,
|
||||
'partner_id': user_in_other_company.partner_id.id,
|
||||
'pricelist_id': pricelist.id,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
|
|
@ -455,7 +515,7 @@ class TestSalePrices(SaleCommon):
|
|||
# product_2.cost_currency_id != so currency
|
||||
pricelist.currency_id = main_curr
|
||||
sales_order = product_1_ctxt.with_context(mail_notrack=True, mail_create_nolog=True).env['sale.order'].create({
|
||||
'partner_id': self.env.user.partner_id.id,
|
||||
'partner_id': user_in_other_company.partner_id.id,
|
||||
'pricelist_id': pricelist.id,
|
||||
'order_line': [
|
||||
# Verify discount is considered in create hack
|
||||
|
|
@ -485,6 +545,7 @@ class TestSalePrices(SaleCommon):
|
|||
"""
|
||||
sale_order = self.sale_order
|
||||
so_amount = sale_order.amount_total
|
||||
start_so_amount = so_amount
|
||||
sale_order._recompute_prices()
|
||||
self.assertEqual(
|
||||
sale_order.amount_total, so_amount,
|
||||
|
|
@ -492,42 +553,77 @@ class TestSalePrices(SaleCommon):
|
|||
|
||||
pricelist = sale_order.pricelist_id
|
||||
pricelist.item_ids = [
|
||||
fields.Command.create({
|
||||
Command.create({
|
||||
'percent_price': 5.0,
|
||||
'compute_price': 'percentage'
|
||||
})
|
||||
]
|
||||
pricelist.discount_policy = "without_discount"
|
||||
sale_order._recompute_prices()
|
||||
|
||||
self.assertTrue(all(line.discount == 5 for line in sale_order.order_line))
|
||||
self.assertEqual(sale_order.amount_undiscounted, so_amount)
|
||||
self.assertEqual(sale_order.amount_total, 0.95*so_amount)
|
||||
|
||||
pricelist.discount_policy = "with_discount"
|
||||
pricelist.item_ids = [
|
||||
Command.create({
|
||||
'price_discount': 5,
|
||||
'compute_price': 'formula',
|
||||
})
|
||||
]
|
||||
sale_order._recompute_prices()
|
||||
|
||||
self.assertTrue(all(line.discount == 0 for line in sale_order.order_line))
|
||||
self.assertEqual(sale_order.amount_undiscounted, so_amount)
|
||||
self.assertEqual(sale_order.amount_total, 0.95*so_amount)
|
||||
|
||||
# Test taking off the pricelist
|
||||
sale_order.pricelist_id = False
|
||||
sale_order._recompute_prices()
|
||||
|
||||
self.assertTrue(all(line.discount == 0 for line in sale_order.order_line))
|
||||
self.assertEqual(sale_order.amount_undiscounted, so_amount)
|
||||
self.assertEqual(
|
||||
sale_order.amount_total, start_so_amount,
|
||||
"The SO amount without pricelist should be the same than with an empty pricelist"
|
||||
)
|
||||
|
||||
def test_manual_price_prevents_recompute(self):
|
||||
sale_order_line = self.sale_order.order_line[0]
|
||||
# Ensure initial price is set correctly
|
||||
self.assertEqual(sale_order_line.price_unit, 20.0)
|
||||
|
||||
# Update the price manually and then change the quantity
|
||||
with Form(sale_order_line) as line:
|
||||
line.price_unit = 100.0
|
||||
line.product_uom_qty = 10
|
||||
|
||||
self.assertEqual(
|
||||
sale_order_line.price_unit, 100.0,
|
||||
"Price should remain 100.0 after changing the quantity"
|
||||
)
|
||||
|
||||
zero_price_product = self._create_product(list_price=0.0)
|
||||
self.assertEqual(zero_price_product.list_price, 0.0)
|
||||
so_line = self.env['sale.order.line'].create({
|
||||
'product_id': zero_price_product.id,
|
||||
'order_id': self.sale_order.id,
|
||||
})
|
||||
self.assertEqual(so_line.price_unit, 0.0)
|
||||
self.assertEqual(so_line.technical_price_unit, 0.0)
|
||||
|
||||
with Form(so_line) as so_line:
|
||||
so_line.price_unit = 10.0
|
||||
so_line.product_uom_qty = 2.0
|
||||
so_line.save()
|
||||
|
||||
self.assertEqual(so_line.price_unit, 10.0)
|
||||
|
||||
# Taxes tests:
|
||||
# We do not rely on accounting common on purpose to avoid
|
||||
# all the useless setup not needed here.
|
||||
# If you need the accounting common (journals, ...), use/make another test class
|
||||
|
||||
def test_sale_tax_mapping(self):
|
||||
tax_a, tax_b = self.env['account.tax'].create([{
|
||||
'name': 'Test tax A',
|
||||
'type_tax_use': 'sale',
|
||||
'price_include': True,
|
||||
'amount': 15.0,
|
||||
}, {
|
||||
'name': 'Test tax B',
|
||||
'type_tax_use': 'sale',
|
||||
'amount': 6.0,
|
||||
}])
|
||||
|
||||
country_belgium = self.env['res.country'].search([
|
||||
('name', '=', 'Belgium'),
|
||||
], limit=1)
|
||||
|
|
@ -535,11 +631,19 @@ class TestSalePrices(SaleCommon):
|
|||
'name': 'Test Fiscal Position',
|
||||
'auto_apply': True,
|
||||
'country_id': country_belgium.id,
|
||||
'tax_ids': [Command.create({
|
||||
'tax_src_id': tax_a.id,
|
||||
'tax_dest_id': tax_b.id
|
||||
})]
|
||||
})
|
||||
tax_a, tax_b = self.env['account.tax'].create([{
|
||||
'name': 'Test tax A',
|
||||
'type_tax_use': 'sale',
|
||||
'price_include_override': 'tax_included',
|
||||
'amount': 15.0,
|
||||
}, {
|
||||
'name': 'Test tax B',
|
||||
'type_tax_use': 'sale',
|
||||
'amount': 6.0,
|
||||
'fiscal_position_ids': fiscal_pos,
|
||||
}])
|
||||
tax_b.original_tax_ids = tax_a
|
||||
|
||||
# setting up partner:
|
||||
self.partner.country_id = country_belgium
|
||||
|
|
@ -550,7 +654,6 @@ class TestSalePrices(SaleCommon):
|
|||
})
|
||||
|
||||
self.pricelist.write({
|
||||
'discount_policy': 'without_discount',
|
||||
'item_ids': [Command.create({
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'percentage',
|
||||
|
|
@ -584,7 +687,7 @@ class TestSalePrices(SaleCommon):
|
|||
"Wrong subtotal price computed for specified product & pricelist"
|
||||
)
|
||||
self.assertEqual(
|
||||
self.empty_order.order_line.tax_id.id, tax_b.id,
|
||||
self.empty_order.order_line.tax_ids.id, tax_b.id,
|
||||
"Wrong tax applied for specified product & pricelist"
|
||||
)
|
||||
|
||||
|
|
@ -598,6 +701,25 @@ class TestSalePrices(SaleCommon):
|
|||
pricelist = self.pricelist
|
||||
partner = self.partner
|
||||
|
||||
(
|
||||
fpos_incl_incl,
|
||||
fpos_excl_incl,
|
||||
fpos_incl_excl,
|
||||
fpos_excl_excl,
|
||||
) = self.env['account.fiscal.position'].create([{
|
||||
'name': "incl -> incl",
|
||||
'sequence': 1,
|
||||
}, {
|
||||
'name': "excl -> incl",
|
||||
'sequence': 2,
|
||||
}, {
|
||||
'name': "incl -> excl",
|
||||
'sequence': 3,
|
||||
}, {
|
||||
'name': "excl -> excl",
|
||||
'sequence': 4,
|
||||
}])
|
||||
|
||||
(
|
||||
tax_fixed_incl,
|
||||
tax_fixed_excl,
|
||||
|
|
@ -607,36 +729,45 @@ class TestSalePrices(SaleCommon):
|
|||
tax_exclude_dst,
|
||||
) = self.env['account.tax'].create([{
|
||||
'name': "fixed include",
|
||||
'amount': '10.00',
|
||||
'amount': 10.00,
|
||||
'amount_type': 'fixed',
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
}, {
|
||||
'name': "fixed exclude",
|
||||
'amount': '10.00',
|
||||
'amount': 10.00,
|
||||
'amount_type': 'fixed',
|
||||
'price_include': False,
|
||||
'price_include_override': 'tax_excluded',
|
||||
}, {
|
||||
'name': "Include 21%",
|
||||
'amount': 21.00,
|
||||
'amount_type': 'percent',
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
}, {
|
||||
'name': "Include 6%",
|
||||
'amount': 6.00,
|
||||
'amount_type': 'percent',
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
}, {
|
||||
'name': "Exclude 15%",
|
||||
'amount': 15.00,
|
||||
'amount_type': 'percent',
|
||||
'price_include': False,
|
||||
'price_include_override': 'tax_excluded',
|
||||
}, {
|
||||
'name': "Exclude 21%",
|
||||
'amount': 21.00,
|
||||
'amount_type': 'percent',
|
||||
'price_include': False,
|
||||
'price_include_override': 'tax_excluded',
|
||||
}])
|
||||
|
||||
tax_include_dst.write({
|
||||
'fiscal_position_ids': fpos_incl_incl | fpos_excl_incl,
|
||||
'original_tax_ids': tax_include_src | tax_exclude_src,
|
||||
})
|
||||
tax_exclude_dst.write({
|
||||
'fiscal_position_ids': fpos_incl_excl | fpos_excl_excl,
|
||||
'original_tax_ids': tax_include_src | tax_exclude_src,
|
||||
})
|
||||
|
||||
(
|
||||
product_tmpl_a,
|
||||
product_tmpl_b,
|
||||
|
|
@ -660,41 +791,6 @@ class TestSalePrices(SaleCommon):
|
|||
'taxes_id': [Command.set([tax_fixed_excl.id, tax_include_src.id])]
|
||||
}])
|
||||
|
||||
(
|
||||
fpos_incl_incl,
|
||||
fpos_excl_incl,
|
||||
fpos_incl_excl,
|
||||
fpos_excl_excl,
|
||||
) = self.env['account.fiscal.position'].create([{
|
||||
'name': "incl -> incl",
|
||||
'sequence': 1,
|
||||
'tax_ids': [Command.create({
|
||||
'tax_src_id': tax_include_src.id,
|
||||
'tax_dest_id': tax_include_dst.id,
|
||||
})]
|
||||
}, {
|
||||
'name': "excl -> incl",
|
||||
'sequence': 2,
|
||||
'tax_ids': [Command.create({
|
||||
'tax_src_id': tax_exclude_src.id,
|
||||
'tax_dest_id': tax_include_dst.id,
|
||||
})]
|
||||
}, {
|
||||
'name': "incl -> excl",
|
||||
'sequence': 3,
|
||||
'tax_ids': [Command.create({
|
||||
'tax_src_id': tax_include_src.id,
|
||||
'tax_dest_id': tax_exclude_dst.id,
|
||||
})]
|
||||
}, {
|
||||
'name': "excl -> excp",
|
||||
'sequence': 4,
|
||||
'tax_ids': [Command.create({
|
||||
'tax_src_id': tax_exclude_src.id,
|
||||
'tax_dest_id': tax_exclude_dst.id,
|
||||
})]
|
||||
}])
|
||||
|
||||
# Create the SO with one SO line and apply a pricelist and fiscal position on it
|
||||
# Then check if price unit and price subtotal matches the expected values
|
||||
|
||||
|
|
@ -775,31 +871,29 @@ class TestSalePrices(SaleCommon):
|
|||
def test_so_tax_mapping(self):
|
||||
order = self.empty_order
|
||||
|
||||
fpos = self.env['account.fiscal.position'].create({
|
||||
'name': 'Test Fiscal Position',
|
||||
'sequence': 1,
|
||||
})
|
||||
|
||||
tax_include, tax_exclude = self.env['account.tax'].create([{
|
||||
'name': 'Include Tax',
|
||||
'amount': '21.00',
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
'type_tax_use': 'sale',
|
||||
}, {
|
||||
'name': 'Exclude Tax',
|
||||
'amount': '0.00',
|
||||
'type_tax_use': 'sale',
|
||||
'fiscal_position_ids': fpos,
|
||||
}])
|
||||
tax_exclude.original_tax_ids = tax_include
|
||||
|
||||
self.product.write({
|
||||
'list_price': 121,
|
||||
'taxes_id': [Command.set(tax_include.ids)]
|
||||
})
|
||||
|
||||
fpos = self.env['account.fiscal.position'].create({
|
||||
'name': 'Test Fiscal Position',
|
||||
'sequence': 1,
|
||||
'tax_ids': [Command.create({
|
||||
'tax_src_id': tax_include.id,
|
||||
'tax_dest_id': tax_exclude.id,
|
||||
})],
|
||||
})
|
||||
|
||||
order.write({
|
||||
'fiscal_position_id': fpos.id,
|
||||
'order_line': [Command.create({
|
||||
|
|
@ -812,6 +906,46 @@ class TestSalePrices(SaleCommon):
|
|||
100, order.order_line[0].price_unit,
|
||||
"The included tax must be subtracted to the price")
|
||||
|
||||
def test_so_tax_mapping_multicompany(self):
|
||||
fpos = self.env['account.fiscal.position'].create({'name': "B2B"})
|
||||
tax_group = self.env['account.tax.group'].create({'name': "10%"})
|
||||
tax_include = self.env['account.tax'].create({
|
||||
'name': "10% Tax Inc.",
|
||||
'type_tax_use': 'sale',
|
||||
'amount': 10.0,
|
||||
'price_include_override': 'tax_included',
|
||||
'tax_group_id': tax_group.id,
|
||||
'fiscal_position_ids': fpos.ids,
|
||||
})
|
||||
tax_exclude = tax_include.copy({
|
||||
'name': "10% Tax Exc.",
|
||||
'amount': 0.0,
|
||||
'price_include_override': 'tax_excluded',
|
||||
'original_tax_ids': tax_include.ids,
|
||||
})
|
||||
self.product.write({
|
||||
'list_price': 110.0,
|
||||
'taxes_id': tax_include.ids,
|
||||
})
|
||||
branch_company = self.env['res.company'].create({
|
||||
'name': "Branch Co.",
|
||||
'parent_id': self.env.company.id,
|
||||
'account_fiscal_country_id': self.env.company.account_fiscal_country_id.id,
|
||||
})
|
||||
order = self.empty_order.with_company(branch_company)
|
||||
order.sudo().write({
|
||||
'company_id': branch_company.id,
|
||||
'fiscal_position_id': fpos.id,
|
||||
'user_id': False,
|
||||
'team_id': False,
|
||||
'order_line': [Command.create({'product_id': self.product.id})],
|
||||
})
|
||||
self.assertEqual(order.order_line.tax_ids, tax_exclude, "Line tax should be mapped")
|
||||
self.assertAlmostEqual(
|
||||
order.order_line.price_unit, 100.0,
|
||||
msg="Tax should not be included in unit price",
|
||||
)
|
||||
|
||||
def test_free_product_and_price_include_fixed_tax(self):
|
||||
""" Check that fixed tax include are correctly computed while the price_unit is 0 """
|
||||
taxes = self.env['account.tax'].create([{
|
||||
|
|
@ -819,14 +953,14 @@ class TestSalePrices(SaleCommon):
|
|||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.05,
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
'include_base_amount': True,
|
||||
}, {
|
||||
'name': 'Recupel 0.25',
|
||||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.25,
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
'include_base_amount': True,
|
||||
}])
|
||||
order = self.empty_order
|
||||
|
|
@ -834,7 +968,7 @@ class TestSalePrices(SaleCommon):
|
|||
'product_id': self.product.id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': 0.0,
|
||||
'tax_id': [
|
||||
'tax_ids': [
|
||||
Command.set(taxes.ids),
|
||||
],
|
||||
})]
|
||||
|
|
@ -855,18 +989,18 @@ class TestSalePrices(SaleCommon):
|
|||
tax_include, tax_exclude = self.env['account.tax'].create([{
|
||||
'name': 'Tax with price include',
|
||||
'amount': 10,
|
||||
'price_include': True
|
||||
'price_include_override': 'tax_included',
|
||||
}, {
|
||||
'name': 'Tax with no price include',
|
||||
'amount': 10,
|
||||
}])
|
||||
|
||||
# Apply taxes on the sale order lines
|
||||
self.sale_order.order_line[0].write({'tax_id': [Command.link(tax_include.id)]})
|
||||
self.sale_order.order_line[1].write({'tax_id': [Command.link(tax_exclude.id)]})
|
||||
self.sale_order.order_line[0].write({'tax_ids': [Command.link(tax_include.id)]})
|
||||
self.sale_order.order_line[1].write({'tax_ids': [Command.link(tax_exclude.id)]})
|
||||
|
||||
for line in self.sale_order.order_line:
|
||||
if line.tax_id.price_include:
|
||||
if line.tax_ids.price_include:
|
||||
price = line.price_unit * line.product_uom_qty - line.price_tax
|
||||
else:
|
||||
price = line.price_unit * line.product_uom_qty
|
||||
|
|
@ -902,11 +1036,11 @@ class TestSalePrices(SaleCommon):
|
|||
# Same with an included-in-price tax
|
||||
order = order.copy()
|
||||
line = order.order_line
|
||||
line.tax_id = [Command.create({
|
||||
line.tax_ids = [Command.create({
|
||||
'name': 'Super Tax',
|
||||
'amount_type': 'percent',
|
||||
'amount': 15.0,
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
})]
|
||||
order.action_confirm()
|
||||
self.assertEqual(line.untaxed_amount_to_invoice, 0)
|
||||
|
|
@ -938,6 +1072,7 @@ class TestSalePrices(SaleCommon):
|
|||
order_line.write({
|
||||
'product_uom_qty': 3.0,
|
||||
'price_unit': 100.0,
|
||||
'discount': 1.0,
|
||||
})
|
||||
order.invalidate_recordset(['amount_undiscounted'])
|
||||
|
||||
|
|
@ -952,11 +1087,11 @@ class TestSalePrices(SaleCommon):
|
|||
# Same with an included-in-price tax
|
||||
order = order.copy()
|
||||
line = order.order_line
|
||||
line.tax_id = [Command.create({
|
||||
line.tax_ids = [Command.create({
|
||||
'name': 'Super Tax',
|
||||
'amount_type': 'percent',
|
||||
'amount': 10.0,
|
||||
'price_include': True,
|
||||
'price_include_override': 'tax_included',
|
||||
})]
|
||||
line.discount = 50.0
|
||||
order.action_confirm()
|
||||
|
|
@ -979,7 +1114,134 @@ class TestSalePrices(SaleCommon):
|
|||
})]
|
||||
order.action_confirm()
|
||||
line = order.order_line
|
||||
quantity_precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
expected_price_subtotal = line.price_unit * float_round(product_uom_qty, precision_digits=quantity_precision)
|
||||
quantity_precision = self.env['decimal.precision'].precision_get('Product Unit')
|
||||
self.assertEqual(
|
||||
line.product_uom_qty, float_round(product_uom_qty, precision_digits=quantity_precision))
|
||||
expected_price_subtotal = line.currency_id.round(
|
||||
line.price_unit * float_round(product_uom_qty, precision_digits=quantity_precision))
|
||||
self.assertAlmostEqual(line.price_subtotal, expected_price_subtotal)
|
||||
self.assertEqual(order.amount_total, order.tax_totals.get('amount_total'))
|
||||
self.assertEqual(order.amount_total, order.tax_totals.get('total_amount_currency'))
|
||||
|
||||
def test_show_discount(self):
|
||||
"""
|
||||
Test that discount is shown only when compute_price is percentage
|
||||
If compute_price is formula, discount should be included in price.
|
||||
"""
|
||||
test_product_discount = self.env['product.product'].create({
|
||||
'name': 'Test Product',
|
||||
'list_price': 100.0,
|
||||
'taxes_id': None,
|
||||
})
|
||||
test_product_incl_discount = self.env['product.product'].create({
|
||||
'name': 'Test Product',
|
||||
'list_price': 100.0,
|
||||
'taxes_id': None,
|
||||
})
|
||||
sale_order = self.env['sale.order'].create({
|
||||
'partner_id': self.partner.id,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
'product_id': test_product_discount.id,
|
||||
'product_uom_qty': 1.0,
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': test_product_incl_discount.id,
|
||||
'product_uom_qty': 1,
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
self.assertEqual(200, sale_order.amount_total)
|
||||
base_discount_pricelist = self.env['product.pricelist'].create({
|
||||
'name': 'Base Discount Pricelist',
|
||||
'item_ids': [
|
||||
Command.create({
|
||||
'name': 'Discount',
|
||||
'applied_on': '1_product',
|
||||
'product_tmpl_id': test_product_discount.product_tmpl_id.id,
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': 10,
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Formula',
|
||||
'applied_on': '1_product',
|
||||
'product_tmpl_id': test_product_incl_discount.product_tmpl_id.id,
|
||||
'compute_price': 'formula',
|
||||
'price_discount': 10,
|
||||
}),
|
||||
]})
|
||||
|
||||
sale_order.pricelist_id = base_discount_pricelist
|
||||
sale_order._recompute_prices()
|
||||
show_discount_line = sale_order.order_line[0]
|
||||
included_discount_line = sale_order.order_line[1]
|
||||
|
||||
self.assertEqual(show_discount_line.price_unit, 100)
|
||||
self.assertEqual(show_discount_line.price_subtotal, show_discount_line.price_unit * 0.9)
|
||||
self.assertEqual(show_discount_line.discount, 10)
|
||||
self.assertEqual(included_discount_line.price_unit, included_discount_line.price_subtotal)
|
||||
self.assertEqual(included_discount_line.discount, 0)
|
||||
|
||||
# Test with discount based on other pricelist
|
||||
discount_pricelist = self.env['product.pricelist'].create({
|
||||
'name': 'Discount Pricelist',
|
||||
'item_ids': [
|
||||
Command.create({
|
||||
'name': 'Discount based on pricelist',
|
||||
'applied_on': '1_product',
|
||||
'product_tmpl_id': test_product_discount.product_tmpl_id.id,
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': 10,
|
||||
'base': 'pricelist',
|
||||
'base_pricelist_id': base_discount_pricelist.id,
|
||||
}),
|
||||
]})
|
||||
sale_order.pricelist_id = discount_pricelist
|
||||
sale_order._recompute_prices()
|
||||
|
||||
self.assertEqual(show_discount_line.price_unit, 100)
|
||||
self.assertEqual(show_discount_line.price_subtotal, show_discount_line.price_unit * 0.81)
|
||||
self.assertEqual(show_discount_line.discount, 19)
|
||||
|
||||
def test_combo_product_discount(self):
|
||||
"""Ensure that pricelist discounts for combo products get applied to combo items"""
|
||||
order = self.empty_order
|
||||
|
||||
product_a = self._create_product(name="Beefy burger")
|
||||
product_b = self._create_product(name="Belgian fries")
|
||||
combos = self.env['product.combo'].create([{
|
||||
'name': "Burger",
|
||||
'combo_item_ids': [Command.create({'product_id': product_a.id})],
|
||||
}, {
|
||||
'name': "Side",
|
||||
'combo_item_ids': [Command.create({'product_id': product_b.id})],
|
||||
}])
|
||||
product_combo = self._create_product(
|
||||
name="Meal Menu",
|
||||
list_price=10.0,
|
||||
type='combo',
|
||||
combo_ids=[Command.set(combos.ids)],
|
||||
)
|
||||
|
||||
self._create_discount_pricelist_rule(product_tmpl_id=product_combo.product_tmpl_id.id)
|
||||
combo_line = self.env['sale.order.line'].create({
|
||||
'order_id': order.id,
|
||||
'product_id': product_combo.id,
|
||||
})
|
||||
item_lines = self.env['sale.order.line'].create([{
|
||||
'order_id': order.id,
|
||||
'product_id': product.id,
|
||||
'combo_item_id': combo.combo_item_ids.id,
|
||||
'linked_line_id': combo_line.id,
|
||||
} for product, combo in zip(product_a + product_b, combos)])
|
||||
|
||||
self.assertEqual(
|
||||
item_lines.mapped('discount'),
|
||||
[self.discount, self.discount],
|
||||
"Discount should apply to combo item lines",
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
order.amount_untaxed,
|
||||
order.amount_undiscounted * (100 - self.discount) / 100,
|
||||
msg="Pricelist discount should be applied to quotation",
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue