sale_management/tests/test_sale_order.py

339 lines
13 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.fields import Command
from odoo.tests import Form, tagged
from odoo.addons.sale_management.tests.common import SaleManagementCommon
@tagged('-at_install', 'post_install')
class TestSaleOrder(SaleManagementCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
# some variables to ease asserts in tests
cls.pub_product_price = 100.0
cls.pl_product_price = 80.0
cls.tpl_discount = 10.0
cls.pl_discount = (cls.pub_product_price - cls.pl_product_price) * 100 / cls.pub_product_price
cls.merged_discount = 100.0 - (100.0 - cls.pl_discount) * (100.0 - cls.tpl_discount) / 100.0
cls.pub_option_price = 200.0
cls.pl_option_price = 100.0
cls.tpl_option_discount = 20.0
cls.pl_option_discount = (cls.pub_option_price - cls.pl_option_price) * 100 / cls.pub_option_price
cls.merged_option_discount = 100.0 - (100.0 - cls.pl_option_discount) * (100.0 - cls.tpl_option_discount) / 100.0
# create some products
cls.product_1, cls.optional_product = cls.env['product.product'].create([
{
'name': 'Product 1',
'lst_price': cls.pub_product_price,
}, {
'name': 'Optional product',
'lst_price': cls.pub_option_price,
}
])
# create some quotation templates
cls.quotation_template_no_discount = cls.env['sale.order.template'].create({
'name': 'A quotation template',
'sale_order_template_line_ids': [
Command.create({
'product_id': cls.product_1.id,
}),
],
'sale_order_template_option_ids': [
Command.create({
'product_id': cls.optional_product.id,
}),
],
})
# create two pricelist with different discount policies (same total price)
pricelist_rule_values = [
Command.create({
'name': 'Product 1 premium price',
'applied_on': '1_product',
'product_tmpl_id': cls.product_1.product_tmpl_id.id,
'compute_price': 'fixed',
'fixed_price': cls.pl_product_price,
}),
Command.create({
'name': 'Optional product premium price',
'applied_on': '1_product',
'product_tmpl_id': cls.optional_product.product_tmpl_id.id,
'compute_price': 'fixed',
'fixed_price': cls.pl_option_price,
}),
]
(
cls.discount_included_price_list,
cls.discount_excluded_price_list
) = cls.env['product.pricelist'].create([
{
'name': 'Discount included Pricelist',
'discount_policy': 'with_discount',
'item_ids': pricelist_rule_values,
}, {
'name': 'Discount excluded Pricelist',
'discount_policy': 'without_discount',
'item_ids': pricelist_rule_values,
}
])
# variable kept to reduce code diff
cls.sale_order = cls.empty_order
def test_01_template_without_pricelist(self):
"""
This test checks that without any rule in the pricelist, the public price
of the product is used in the sale order after selecting a
quotation template.
"""
# first case, without discount in the quotation template
self.sale_order.write({
'sale_order_template_id': self.quotation_template_no_discount.id
})
self.sale_order._onchange_sale_order_template_id()
self.assertEqual(
len(self.sale_order.order_line),
1,
"The sale order shall contains the same number of products as"
"the quotation template.")
self.assertEqual(
self.sale_order.order_line[0].product_id.id,
self.product_1.id,
"The sale order shall contains the same products as the"
"quotation template.")
self.assertEqual(
self.sale_order.order_line[0].price_unit,
self.pub_product_price,
"Without any price list and discount, the public price of"
"the product shall be used.")
self.assertEqual(
len(self.sale_order.sale_order_option_ids),
1,
"The sale order shall contains the same number of optional products as"
"the quotation template.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].product_id.id,
self.optional_product.id,
"The sale order shall contains the same optional products as the"
"quotation template.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].price_unit,
self.pub_option_price,
"Without any price list and discount, the public price of"
"the optional product shall be used.")
# add the option to the order
self.sale_order.sale_order_option_ids[0].button_add_to_order()
self.assertEqual(
len(self.sale_order.order_line),
2,
"When an option is added, a new order line is created")
self.assertEqual(
self.sale_order.order_line[1].product_id.id,
self.optional_product.id,
"The sale order shall contains the same products as the"
"quotation template.")
self.assertEqual(
self.sale_order.order_line[1].price_unit,
self.pub_option_price,
"Without any price list and discount, the public price of"
"the optional product shall be used.")
def test_02_template_with_discount_included_pricelist(self):
"""
This test checks that with a 'discount included' price list,
the price used in the sale order is computed according to the
price list.
"""
# first case, without discount in the quotation template
self.sale_order.write({
'pricelist_id': self.discount_included_price_list.id,
'sale_order_template_id': self.quotation_template_no_discount.id
})
self.sale_order._onchange_sale_order_template_id()
self.assertEqual(
self.sale_order.order_line[0].price_unit,
self.pl_product_price,
"If a pricelist is set, the product price shall be computed"
"according to it.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].price_unit,
self.pl_option_price,
"If a pricelist is set, the optional product price shall"
"be computed according to it.")
# add the option to the order
self.sale_order.sale_order_option_ids[0].button_add_to_order()
self.assertEqual(
self.sale_order.order_line[1].price_unit,
self.pl_option_price,
"If a pricelist is set, the optional product price shall"
"be computed according to it.")
def test_03_template_with_discount_excluded_pricelist(self):
"""
This test checks that with a 'discount excluded' price list,
the price used in the sale order is the product public price and
the discount is computed according to the price list.
"""
self.sale_order.write({
'pricelist_id': self.discount_excluded_price_list.id,
'sale_order_template_id': self.quotation_template_no_discount.id
})
self.sale_order._onchange_sale_order_template_id()
self.assertEqual(
self.sale_order.order_line[0].price_unit,
self.pub_product_price,
"If a pricelist is set without discount included, the unit "
"price shall be the public product price.")
self.assertEqual(
self.sale_order.order_line[0].price_subtotal,
self.pl_product_price,
"If a pricelist is set without discount included, the subtotal "
"price shall be the price computed according to the price list.")
self.assertEqual(
self.sale_order.order_line[0].discount,
self.pl_discount,
"If a pricelist is set without discount included, the discount "
"shall be computed according to the price unit and the subtotal."
"price")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].price_unit,
self.pub_option_price,
"If a pricelist is set without discount included, the unit "
"price shall be the public optional product price.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].discount,
self.pl_option_discount,
"If a pricelist is set without discount included, the discount "
"shall be computed according to the optional price unit and"
"the subtotal price.")
# add the option to the order
self.sale_order.sale_order_option_ids[0].button_add_to_order()
self.assertEqual(
self.sale_order.order_line[1].price_unit,
self.pub_option_price,
"If a pricelist is set without discount included, the unit "
"price shall be the public optional product price.")
self.assertEqual(
self.sale_order.order_line[1].price_subtotal,
self.pl_option_price,
"If a pricelist is set without discount included, the subtotal "
"price shall be the price computed according to the price list.")
self.assertEqual(
self.sale_order.order_line[1].discount,
self.pl_option_discount,
"If a pricelist is set without discount included, the discount "
"shall be computed according to the price unit and the subtotal."
"price")
def test_04_update_pricelist_option_line(self):
"""
This test checks that option line's values are correctly
updated after a pricelist update
"""
self.sale_order.write({
'sale_order_template_id': self.quotation_template_no_discount.id
})
self.sale_order._onchange_sale_order_template_id()
self.assertEqual(
self.sale_order.sale_order_option_ids[0].price_unit,
self.pub_option_price,
"If no pricelist is set, the unit price shall be the option's product price.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].discount, 0,
"If no pricelist is set, the discount should be 0.")
self.sale_order.write({
'pricelist_id': self.discount_included_price_list.id,
})
self.sale_order._recompute_prices()
self.assertEqual(
self.sale_order.sale_order_option_ids[0].price_unit,
self.pl_option_price,
"If a pricelist is set with discount included,"
" the unit price shall be the option's product discounted price.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].discount, 0,
"If a pricelist is set with discount included,"
" the discount should be 0.")
self.sale_order.write({
'pricelist_id': self.discount_excluded_price_list.id,
})
self.sale_order._recompute_prices()
self.assertEqual(
self.sale_order.sale_order_option_ids[0].price_unit,
self.pub_option_price,
"If a pricelist is set without discount included,"
" the unit price shall be the option's product sale price.")
self.assertEqual(
self.sale_order.sale_order_option_ids[0].discount,
self.pl_option_discount,
"If a pricelist is set without discount included,"
" the discount should be correctly computed.")
def test_option_creation(self):
"""Make sure the product uom is automatically added to the option when the product is specified"""
order_form = Form(self.sale_order)
with order_form.sale_order_option_ids.new() as option:
option.product_id = self.product_1
order = order_form.save()
self.assertTrue(bool(order.sale_order_option_ids.uom_id))
def test_option_price_unit_is_not_recomputed(self):
"""
Verifies that user defined price unit for optional products remains the same after
update of quantities.
"""
sale_order_with_option = self.env['sale.order'].create({
'partner_id': self.partner.id,
'sale_order_option_ids': [Command.create({
'product_id': self.optional_product.id,
'price_unit': 10,
})],
})
sale_order_with_option.sale_order_option_ids.add_option_to_order()
# after changing the quantity of the product, the price unit should not be recomputed
sale_order_with_option.order_line.product_uom_qty = 10
self.assertEqual(sale_order_with_option.sale_order_option_ids.price_unit, 10)