313 lines
14 KiB
Python
313 lines
14 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from datetime import datetime
|
||
|
import time
|
||
|
|
||
|
from odoo.fields import Command, first
|
||
|
from odoo.tools import float_compare
|
||
|
|
||
|
from odoo.addons.product.tests.common import ProductCommon
|
||
|
|
||
|
|
||
|
class TestProductPricelist(ProductCommon):
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super().setUpClass()
|
||
|
|
||
|
cls.category_5_id = cls.env['product.category'].create({
|
||
|
'name': 'Office Furniture',
|
||
|
'parent_id': cls.product_category.id
|
||
|
}).id
|
||
|
cls.computer_SC234 = cls.env['product.product'].create({
|
||
|
'name': 'Desk Combination',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
})
|
||
|
cls.ipad_retina_display = cls.env['product.product'].create({
|
||
|
'name': 'Customizable Desk',
|
||
|
})
|
||
|
cls.custom_computer_kit = cls.env['product.product'].create({
|
||
|
'name': 'Corner Desk Right Sit',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
})
|
||
|
cls.ipad_mini = cls.env['product.product'].create({
|
||
|
'name': 'Large Cabinet',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
'standard_price': 800.0,
|
||
|
})
|
||
|
cls.monitor = cls.env['product.product'].create({
|
||
|
'name': 'Super nice monitor',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
'list_price': 1000.0,
|
||
|
})
|
||
|
|
||
|
cls.apple_in_ear_headphones = cls.env['product.product'].create({
|
||
|
'name': 'Storage Box',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
})
|
||
|
cls.laptop_E5023 = cls.env['product.product'].create({
|
||
|
'name': 'Office Chair',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
})
|
||
|
cls.laptop_S3450 = cls.env['product.product'].create({
|
||
|
'name': 'Acoustic Bloc Screens',
|
||
|
'categ_id': cls.category_5_id,
|
||
|
})
|
||
|
cls.product_multi_price = cls.env['product.product'].create({
|
||
|
'name': 'Multi Price',
|
||
|
'categ_id': cls.product_category.id,
|
||
|
})
|
||
|
|
||
|
cls.new_currency = cls.env['res.currency'].create({
|
||
|
'name': 'Wonderful Currency',
|
||
|
'symbol': ':)',
|
||
|
'rate_ids': [Command.create({'rate': 10, 'name': time.strftime('%Y-%m-%d')})],
|
||
|
})
|
||
|
|
||
|
cls.ipad_retina_display.write({'uom_id': cls.uom_unit.id, 'categ_id': cls.category_5_id})
|
||
|
cls.customer_pricelist = cls.env['product.pricelist'].create({
|
||
|
'name': 'Customer Pricelist',
|
||
|
'item_ids': [
|
||
|
Command.create({
|
||
|
'name': 'Default pricelist',
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'pricelist',
|
||
|
'base_pricelist_id': cls.pricelist.id,
|
||
|
}),
|
||
|
Command.create({
|
||
|
'name': '10% Discount on Assemble Computer',
|
||
|
'applied_on': '1_product',
|
||
|
'product_tmpl_id': cls.ipad_retina_display.product_tmpl_id.id,
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'list_price',
|
||
|
'price_discount': 10,
|
||
|
}),
|
||
|
Command.create({
|
||
|
'name': '1 surchange on Laptop',
|
||
|
'applied_on': '1_product',
|
||
|
'product_tmpl_id': cls.laptop_E5023.product_tmpl_id.id,
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'list_price',
|
||
|
'price_surcharge': 1,
|
||
|
}),
|
||
|
Command.create({
|
||
|
'name': '5% Discount on all Computer related products',
|
||
|
'applied_on': '2_product_category',
|
||
|
'min_quantity': 2,
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'list_price',
|
||
|
'categ_id': cls.product_category.id,
|
||
|
'price_discount': 5,
|
||
|
}),
|
||
|
Command.create({
|
||
|
'name': '30% Discount on all products',
|
||
|
'applied_on': '3_global',
|
||
|
'date_start': '2011-12-27',
|
||
|
'date_end': '2011-12-31',
|
||
|
'compute_price': 'formula',
|
||
|
'price_discount': 30,
|
||
|
'base': 'list_price',
|
||
|
}),
|
||
|
Command.create({
|
||
|
'name': 'Fixed on all products',
|
||
|
'applied_on': '1_product',
|
||
|
'product_tmpl_id': cls.monitor.product_tmpl_id.id,
|
||
|
'date_start': '2020-04-06 09:00:00',
|
||
|
'date_end': '2020-04-09 12:00:00',
|
||
|
'compute_price': 'formula',
|
||
|
'price_discount': 50,
|
||
|
'base': 'list_price',
|
||
|
}),
|
||
|
Command.create({
|
||
|
'name': 'Multi Price Customer',
|
||
|
'applied_on': '1_product',
|
||
|
'product_tmpl_id': cls.product_multi_price.product_tmpl_id.id,
|
||
|
'compute_price': 'fixed',
|
||
|
'fixed_price': 99,
|
||
|
'base': 'list_price',
|
||
|
}),
|
||
|
],
|
||
|
})
|
||
|
cls.business_pricelist = cls.env['product.pricelist'].create({
|
||
|
'name': 'Business Pricelist',
|
||
|
'item_ids': [(0, 0, {
|
||
|
'name': 'Multi Price Business',
|
||
|
'applied_on': '1_product',
|
||
|
'product_tmpl_id': cls.product_multi_price.product_tmpl_id.id,
|
||
|
'compute_price': 'fixed',
|
||
|
'fixed_price': 50,
|
||
|
'base': 'list_price'
|
||
|
})]
|
||
|
})
|
||
|
|
||
|
def test_10_calculation_price_of_products_pricelist(self):
|
||
|
"""Test calculation of product price based on pricelist"""
|
||
|
# I check sale price of Customizable Desk
|
||
|
context = {}
|
||
|
context.update({'pricelist': self.customer_pricelist.id, 'quantity': 1})
|
||
|
product = self.ipad_retina_display
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0)
|
||
|
msg = "Wrong sale price: Customizable Desk. should be %s instead of %s" % (price, (product.lst_price-product.lst_price*(0.10)))
|
||
|
self.assertEqual(float_compare(
|
||
|
price, (product.lst_price-product.lst_price*(0.10)), precision_digits=2), 0, msg)
|
||
|
|
||
|
# I check sale price of Laptop.
|
||
|
product = self.laptop_E5023
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0)
|
||
|
msg = "Wrong sale price: Laptop. should be %s instead of %s" % (price, (product.lst_price + 1))
|
||
|
self.assertEqual(float_compare(price, product.lst_price + 1, precision_digits=2), 0, msg)
|
||
|
|
||
|
# I check sale price of IT component.
|
||
|
product = self.apple_in_ear_headphones
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0)
|
||
|
msg = "Wrong sale price: IT component. should be %s instead of %s" % (price, product.lst_price)
|
||
|
self.assertEqual(float_compare(price, product.lst_price, precision_digits=2), 0, msg)
|
||
|
|
||
|
# I check sale price of IT component if more than 3 Unit.
|
||
|
context.update({'quantity': 5})
|
||
|
product = self.laptop_S3450
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=5.0)
|
||
|
msg = "Wrong sale price: IT component if more than 3 Unit. should be %s instead of %s" % (price, (product.lst_price-product.lst_price*(0.05)))
|
||
|
self.assertEqual(float_compare(price, product.lst_price-product.lst_price*(0.05), precision_digits=2), 0, msg)
|
||
|
|
||
|
# I check sale price of LCD Monitor.
|
||
|
product = self.ipad_mini
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0)
|
||
|
msg = "Wrong sale price: LCD Monitor. should be %s instead of %s" % (price, product.lst_price)
|
||
|
self.assertEqual(float_compare(price, product.lst_price, precision_digits=2), 0, msg)
|
||
|
|
||
|
# I check sale price of LCD Monitor on end of year.
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0, date='2011-12-31')
|
||
|
msg = "Wrong sale price: LCD Monitor on end of year. should be %s instead of %s" % (price, product.lst_price-product.lst_price*(0.30))
|
||
|
self.assertEqual(float_compare(price, product.lst_price-product.lst_price*(0.30), precision_digits=2), 0, msg)
|
||
|
|
||
|
# Check if the pricelist is applied at precise datetime
|
||
|
product = self.monitor
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0, date='2020-04-05 08:00:00')
|
||
|
context.update({'quantity': 1, 'date': datetime.strptime('2020-04-05 08:00:00', '%Y-%m-%d %H:%M:%S')})
|
||
|
msg = "Wrong cost price: LCD Monitor. should be 1000 instead of %s" % price
|
||
|
self.assertEqual(
|
||
|
float_compare(price, product.lst_price, precision_digits=2), 0,
|
||
|
msg)
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0, date='2020-04-06 10:00:00')
|
||
|
msg = "Wrong cost price: LCD Monitor. should be 500 instead of %s" % price
|
||
|
self.assertEqual(
|
||
|
float_compare(price, product.lst_price/2, precision_digits=2), 0,
|
||
|
msg)
|
||
|
|
||
|
# Check if the price is different when we change the pricelist
|
||
|
product = self.product_multi_price
|
||
|
price = self.customer_pricelist._get_product_price(product, quantity=1.0)
|
||
|
msg = "Wrong price: Multi Product Price. should be 99 instead of %s" % price
|
||
|
self.assertEqual(float_compare(price, 99, precision_digits=2), 0, msg)
|
||
|
|
||
|
price = self.business_pricelist._get_product_price(product, quantity=1.0)
|
||
|
msg = "Wrong price: Multi Product Price. should be 50 instead of %s" % price
|
||
|
self.assertEqual(float_compare(price, 50, precision_digits=2), 0, msg)
|
||
|
|
||
|
def test_20_price_different_currency_pricelist(self):
|
||
|
pricelist = self.env['product.pricelist'].create({
|
||
|
'name': 'Currency Pricelist',
|
||
|
'currency_id': self.new_currency.id,
|
||
|
'item_ids': [(0, 0, {
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'list_price',
|
||
|
'price_surcharge': 100
|
||
|
})]
|
||
|
})
|
||
|
price = pricelist._get_product_price(self.monitor, quantity=1.0)
|
||
|
# product price use the currency of the pricelist
|
||
|
self.assertEqual(price, 10100)
|
||
|
|
||
|
def test_21_price_diff_cur_min_margin_pricelist(self):
|
||
|
pricelist = self.env['product.pricelist'].create({
|
||
|
'name': 'Currency with Margin Pricelist',
|
||
|
'currency_id': self.new_currency.id,
|
||
|
'item_ids': [(0, 0, {
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'list_price',
|
||
|
'price_min_margin': 10,
|
||
|
'price_max_margin': 100,
|
||
|
})]
|
||
|
})
|
||
|
price = pricelist._get_product_price(self.monitor, quantity=1.0)
|
||
|
# product price use the currency of the pricelist
|
||
|
self.assertEqual(price, 10010)
|
||
|
|
||
|
def test_22_price_diff_cur_max_margin_pricelist(self):
|
||
|
pricelist = self.env['product.pricelist'].create({
|
||
|
'name': 'Currency with Margin Pricelist',
|
||
|
'currency_id': self.new_currency.id,
|
||
|
'item_ids': [(0, 0, {
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'list_price',
|
||
|
'price_surcharge': 100,
|
||
|
'price_max_margin': 90
|
||
|
})]
|
||
|
})
|
||
|
price = pricelist._get_product_price(self.monitor, quantity=1.0)
|
||
|
# product price use the currency of the pricelist
|
||
|
self.assertEqual(price, 10090)
|
||
|
|
||
|
def test_price_without_pricelist_fallback_product_price(self):
|
||
|
ProductPricelist = self.env['product.pricelist']
|
||
|
spam = self.env['product.product'].create({
|
||
|
'name': '1 tonne of spam',
|
||
|
'uom_id': self.uom_ton.id,
|
||
|
'uom_po_id': self.uom_ton.id,
|
||
|
'list_price': 100,
|
||
|
'type': 'consu'
|
||
|
})
|
||
|
self.assertEqual(
|
||
|
ProductPricelist._get_product_price(self.monitor, quantity=1.0),
|
||
|
self.monitor.list_price,
|
||
|
msg="without pricelist, the price should be the same as the list price",
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
ProductPricelist._get_product_price(self.monitor, quantity=1.0, currency=self.new_currency),
|
||
|
self.monitor.list_price*10,
|
||
|
msg="without pricelist but with a currency different than the product one, the price "
|
||
|
"should be the same as the list price converted with the currency rate",
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
ProductPricelist._get_product_price(spam, quantity=1.0, uom=self.uom_kgm),
|
||
|
spam.list_price / 1000,
|
||
|
msg="the product price should be converted using the specified uom",
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
ProductPricelist._get_product_price(
|
||
|
spam, quantity=1.0, currency=self.new_currency, uom=self.uom_kgm
|
||
|
),
|
||
|
spam.list_price / 100,
|
||
|
msg="the product price should be converted using the specified uom and converted to the"
|
||
|
" correct currency",
|
||
|
)
|
||
|
|
||
|
def test_30_pricelist_delete(self):
|
||
|
""" Test that `unlink` on many records doesn't raise a RecursionError. """
|
||
|
self.customer_pricelist = self.env['product.pricelist'].create({
|
||
|
'name': 'Customer Pricelist',
|
||
|
'item_ids': [
|
||
|
Command.create({
|
||
|
'compute_price': 'formula',
|
||
|
'base': 'pricelist',
|
||
|
}),
|
||
|
] * 101,
|
||
|
})
|
||
|
self.customer_pricelist.unlink()
|
||
|
|
||
|
def test_40_pricelist_item_min_quantity_precision(self):
|
||
|
"""Test that the min_quantity has the precision of Product UoM."""
|
||
|
# Arrange: Change precision digits
|
||
|
uom_precision = self.env.ref("product.decimal_product_uom")
|
||
|
uom_precision.digits = 3
|
||
|
pricelist_item = first(self.customer_pricelist.item_ids[0])
|
||
|
precise_value = 1.234
|
||
|
|
||
|
# Act: Set a value for the increased precision
|
||
|
pricelist_item.min_quantity = precise_value
|
||
|
|
||
|
# Assert: The set value is kept
|
||
|
self.assertEqual(pricelist_item.min_quantity, precise_value)
|