201 lines
8.2 KiB
Python
201 lines
8.2 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.exceptions import UserError
|
|
from odoo.fields import Command
|
|
from odoo.tests import tagged, Form
|
|
|
|
from odoo.addons.product.tests.common import ProductCommon
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestPricelist(ProductCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.datacard = cls.env['product.product'].create({'name': 'Office Lamp'})
|
|
cls.usb_adapter = cls.env['product.product'].create({'name': 'Office Chair'})
|
|
|
|
cls.sale_pricelist_id = cls.env['product.pricelist'].create({
|
|
'name': 'Sale pricelist',
|
|
'item_ids': [
|
|
Command.create({
|
|
'compute_price': 'formula',
|
|
'base': 'list_price', # based on public price
|
|
'price_discount': 10,
|
|
'product_id': cls.usb_adapter.id,
|
|
'applied_on': '0_product_variant',
|
|
}),
|
|
Command.create({
|
|
'compute_price': 'formula',
|
|
'base': 'list_price', # based on public price
|
|
'price_surcharge': -0.5,
|
|
'product_id': cls.datacard.id,
|
|
'applied_on': '0_product_variant',
|
|
}),
|
|
Command.create({
|
|
'compute_price': 'formula',
|
|
'base': 'standard_price', # based on cost
|
|
'price_markup': 99.99,
|
|
'applied_on': '3_global',
|
|
}),
|
|
],
|
|
})
|
|
# Enable pricelist feature
|
|
cls.env.user.groups_id += cls.env.ref('product.group_product_pricelist')
|
|
|
|
def test_10_discount(self):
|
|
# Make sure the price using a pricelist is the same than without after
|
|
# applying the computation manually
|
|
|
|
self.assertEqual(
|
|
self.pricelist._get_product_price(self.usb_adapter, 1.0)*0.9,
|
|
self.sale_pricelist_id._get_product_price(self.usb_adapter, 1.0))
|
|
|
|
self.assertEqual(
|
|
self.pricelist._get_product_price(self.datacard, 1.0)-0.5,
|
|
self.sale_pricelist_id._get_product_price(self.datacard, 1.0))
|
|
|
|
self.assertAlmostEqual(
|
|
self.sale_pricelist_id._get_product_price(self.usb_adapter, 1.0, uom=self.uom_unit)*12,
|
|
self.sale_pricelist_id._get_product_price(self.usb_adapter, 1.0, uom=self.uom_dozen))
|
|
|
|
# price_surcharge applies to product default UoM, here "Units", so surcharge will be multiplied
|
|
self.assertAlmostEqual(
|
|
self.sale_pricelist_id._get_product_price(self.datacard, 1.0, uom=self.uom_unit)*12,
|
|
self.sale_pricelist_id._get_product_price(self.datacard, 1.0, uom=self.uom_dozen))
|
|
|
|
def test_11_markup(self):
|
|
"""Ensure `price_markup` always equals negative `price_discount`."""
|
|
# Check create values
|
|
for item in self.sale_pricelist_id.item_ids:
|
|
self.assertEqual(item.price_markup, -item.price_discount)
|
|
|
|
# Overwrite create values, and check again
|
|
self.sale_pricelist_id.item_ids[0].price_discount = 0
|
|
self.sale_pricelist_id.item_ids[1].price_discount = -20.02
|
|
self.sale_pricelist_id.item_ids[2].price_markup = -0.5
|
|
for item in self.sale_pricelist_id.item_ids:
|
|
self.assertEqual(item.price_markup, -item.price_discount)
|
|
|
|
def test_20_pricelist_uom(self):
|
|
# Verify that the pricelist rules are correctly using the product's default UoM
|
|
# as reference, and return a result according to the target UoM (as specific in the context)
|
|
|
|
tonne_price = 100
|
|
|
|
# make sure 'tonne' resolves down to 1 'kg'.
|
|
self.uom_ton.write({'rounding': 0.001})
|
|
# setup product stored in 'tonnes', with a discounted pricelist for qty > 3 tonnes
|
|
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': tonne_price,
|
|
'type': 'consu'
|
|
})
|
|
|
|
self.env['product.pricelist.item'].create({
|
|
'pricelist_id': self.pricelist.id,
|
|
'applied_on': '0_product_variant',
|
|
'compute_price': 'formula',
|
|
'base': 'list_price', # based on public price
|
|
'min_quantity': 3, # min = 3 tonnes
|
|
'price_surcharge': -10, # -10 EUR / tonne
|
|
'product_id': spam.id
|
|
})
|
|
|
|
def test_unit_price(qty, uom_id, expected_unit_price):
|
|
uom = self.env['uom.uom'].browse(uom_id)
|
|
unit_price = self.pricelist._get_product_price(spam, qty, uom=uom)
|
|
self.assertAlmostEqual(unit_price, expected_unit_price, msg='Computed unit price is wrong')
|
|
|
|
# Test prices - they are *per unit*, the quantity is only here to match the pricelist rules!
|
|
test_unit_price(2, self.uom_kgm.id, tonne_price / 1000.0)
|
|
test_unit_price(2000, self.uom_kgm.id, tonne_price / 1000.0)
|
|
test_unit_price(3500, self.uom_kgm.id, (tonne_price - 10) / 1000.0)
|
|
test_unit_price(2, self.uom_ton.id, tonne_price)
|
|
test_unit_price(3, self.uom_ton.id, tonne_price - 10)
|
|
|
|
def test_30_pricelists_order(self):
|
|
# Verify the order of pricelists after creation
|
|
|
|
ProductPricelist = self.env['product.pricelist']
|
|
res_partner = self.env['res.partner'].create({'name': 'Ready Corner'})
|
|
|
|
ProductPricelist.search([]).active = False
|
|
|
|
pl_first = ProductPricelist.create({'name': 'First Pricelist'})
|
|
res_partner.invalidate_recordset(['property_product_pricelist'])
|
|
|
|
self.assertEqual(res_partner.property_product_pricelist, pl_first)
|
|
|
|
ProductPricelist.create({'name': 'Second Pricelist'})
|
|
res_partner.invalidate_recordset(['property_product_pricelist'])
|
|
|
|
self.assertEqual(res_partner.property_product_pricelist, pl_first)
|
|
|
|
def test_pricelists_multi_comp_checks(self):
|
|
first_company = self.env.company
|
|
second_company = self.env['res.company'].create({'name': 'Test Company'})
|
|
|
|
shared_pricelist = self.env['product.pricelist'].create({
|
|
'name': 'Test Multi-comp pricelist',
|
|
'company_id': False,
|
|
})
|
|
second_pricelist = self.env['product.pricelist'].create({
|
|
'name': f'Second test pricelist{first_company.name}',
|
|
})
|
|
|
|
self.assertEqual(self.pricelist.company_id, first_company)
|
|
self.assertFalse(shared_pricelist.company_id)
|
|
|
|
with self.assertRaises(UserError):
|
|
shared_pricelist.item_ids = [
|
|
Command.create({
|
|
'compute_price': 'formula',
|
|
'base': 'pricelist',
|
|
'base_pricelist_id': self.pricelist.id,
|
|
})
|
|
]
|
|
|
|
self.pricelist.item_ids = [
|
|
Command.create({
|
|
'compute_price': 'formula',
|
|
'base': 'pricelist',
|
|
'base_pricelist_id': shared_pricelist.id,
|
|
}),
|
|
Command.create({
|
|
'compute_price': 'formula',
|
|
'base': 'pricelist',
|
|
'base_pricelist_id': second_pricelist.id,
|
|
})
|
|
]
|
|
|
|
with self.assertRaises(UserError):
|
|
self.pricelist.company_id = second_company
|
|
|
|
def test_pricelists_res_partner_form(self):
|
|
pricelist_europe = self.env['product.pricelist'].create({
|
|
'name': 'Sale pricelist',
|
|
'country_group_ids': self.env.ref('base.europe').ids,
|
|
})
|
|
|
|
default_pricelist = self.env['product.pricelist'].search([('name', 'ilike', ' ')], limit=1)
|
|
|
|
with Form(self.env['res.partner']) as partner_form:
|
|
partner_form.name = "test"
|
|
self.assertEqual(partner_form.property_product_pricelist, default_pricelist)
|
|
|
|
partner_form.country_id = self.env.ref('base.be')
|
|
self.assertEqual(partner_form.property_product_pricelist, pricelist_europe)
|
|
|
|
partner_form.property_product_pricelist = self.sale_pricelist_id
|
|
self.assertEqual(partner_form.property_product_pricelist, self.sale_pricelist_id)
|
|
|
|
partner = partner_form.save()
|
|
|
|
with Form(partner) as partner_form:
|
|
self.assertEqual(partner_form.property_product_pricelist, self.sale_pricelist_id)
|