forked from Mapan/odoo17e
675 lines
26 KiB
Python
675 lines
26 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import timedelta
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import fields
|
|
from odoo.fields import Command
|
|
from odoo.tools import float_compare
|
|
from odoo.tests import Form, HttpCase, tagged, TransactionCase
|
|
|
|
|
|
class TestRentalCommon(TransactionCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.env.company.country_id = cls.env.ref('base.us')
|
|
cls.env['account.tax.group'].create(
|
|
{'name': 'Test Account Tax Group', 'company_id': cls.env.company.id}
|
|
)
|
|
|
|
cls.product_id = cls.env['product.product'].create({
|
|
'name': 'Projector',
|
|
'categ_id': cls.env.ref('product.product_category_all').id,
|
|
'type': 'consu',
|
|
'rent_ok': True,
|
|
'extra_hourly': 7.0,
|
|
'extra_daily': 30.0,
|
|
})
|
|
|
|
cls.product_template_id = cls.product_id.product_tmpl_id
|
|
|
|
cls.product_template_id.product_pricing_ids.unlink()
|
|
cls.recurrence_hourly = cls.env['sale.temporal.recurrence'].create({'duration': 1.0, 'unit': 'hour'})
|
|
cls.recurrence_5_hours = cls.env['sale.temporal.recurrence'].create({'duration': 5.0, 'unit': 'hour'})
|
|
cls.recurrence_15_hours = cls.env['sale.temporal.recurrence'].create({'duration': 15.0, 'unit': 'hour'})
|
|
cls.recurrence_daily = cls.env['sale.temporal.recurrence'].create({'duration': 1.0, 'unit': 'day'})
|
|
# blank the demo pricings
|
|
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': cls.recurrence_hourly.id,
|
|
'price': 3.5,
|
|
}, {
|
|
'recurrence_id': cls.recurrence_5_hours.id,
|
|
'price': 15.0,
|
|
}, {
|
|
'recurrence_id': cls.recurrence_15_hours.id,
|
|
'price': 40.0,
|
|
}, {
|
|
'recurrence_id': cls.recurrence_daily.id,
|
|
'price': 60.0,
|
|
},
|
|
]
|
|
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=cls.product_template_id.id)
|
|
cls.env['product.pricing'].create(pricing)
|
|
|
|
cls.tax_included = cls.env['account.tax'].create({'name': 'Tax Incl', 'amount': 10, 'price_include': True})
|
|
cls.tax_excluded = cls.env['account.tax'].create({'name': 'Tax Excl', 'amount': 10, 'price_include': False})
|
|
|
|
def test_pricing(self):
|
|
# check pricing returned = expected
|
|
now = fields.Datetime.now()
|
|
self.assertEqual(
|
|
self.product_id._get_best_pricing_rule(
|
|
start_date=now, end_date=now + relativedelta(hours=9)
|
|
)._compute_price(9.0, 'hour'),
|
|
30.0,
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_best_pricing_rule(
|
|
start_date=now, end_date=now + relativedelta(hours=1)
|
|
)._compute_price(11.0, 'hour'),
|
|
38.5,
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_best_pricing_rule(
|
|
start_date=now, end_date=now + relativedelta(hours=16)
|
|
)._compute_price(16.0, 'hour'),
|
|
56.0,
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_best_pricing_rule(
|
|
start_date=now, end_date=now + relativedelta(hours=20)
|
|
)._compute_price(20.0, 'hour'),
|
|
60.0,
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_best_pricing_rule(
|
|
start_date=now, end_date=now + relativedelta(hours=24)
|
|
)._compute_price(24.0, 'hour'),
|
|
60.0,
|
|
)
|
|
|
|
def test_pricing_advanced(self):
|
|
# with pricings applied only to some variants ...
|
|
return
|
|
|
|
def test_pricelists(self):
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
pricelist_A = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist A',
|
|
})
|
|
pricelist_B = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist B',
|
|
})
|
|
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': self.recurrence_hourly.id,
|
|
'price': 3.5,
|
|
'pricelist_id': pricelist_A.id,
|
|
}, {
|
|
'recurrence_id': self.recurrence_5_hours.id,
|
|
'price': 15.0,
|
|
'pricelist_id': pricelist_B.id,
|
|
}
|
|
]
|
|
self.product_template_id.list_price = 42
|
|
self.product_template_id.product_pricing_ids.unlink()
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=self.product_template_id.id)
|
|
pricing = self.env['product.pricing'].create(pricing)
|
|
|
|
reservation_begin = fields.Datetime.now()
|
|
pickup_date = reservation_begin + relativedelta(days=1)
|
|
return_date = pickup_date + relativedelta(hours=1)
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': partner.id,
|
|
'rental_start_date': pickup_date,
|
|
'rental_return_date': return_date,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
'reservation_begin': reservation_begin,
|
|
})
|
|
sol.update({'is_rental': True})
|
|
|
|
sale_order.write({'pricelist_id': pricelist_A.id})
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.price_unit, 3.5, "Pricing should take into account pricelist A")
|
|
sale_order.write({'pricelist_id': pricelist_B.id})
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.price_unit, 15, "Pricing should take into account pricelist B")
|
|
pricelist_B.active = False
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.price_unit, 42, "Pricing should not take into account archived pricelist B")
|
|
|
|
def test_delay_pricing(self):
|
|
# Return Products late and verify duration is correct.
|
|
self.product_id.extra_hourly = 2.5
|
|
self.product_id.extra_daily = 15.0
|
|
|
|
self.assertEqual(
|
|
self.product_id._compute_delay_price(timedelta(hours=5.0)),
|
|
12.5
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._compute_delay_price(timedelta(hours=5.0, days=6)),
|
|
102.5
|
|
)
|
|
|
|
def test_discount(self):
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
pricelist_A = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist A',
|
|
'discount_policy': 'without_discount',
|
|
'company_id': self.env.company.id,
|
|
'item_ids': [(0, 0, {
|
|
'applied_on': '3_global',
|
|
'compute_price': 'percentage',
|
|
'percent_price': 10,
|
|
})],
|
|
})
|
|
pricelist_B = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist B',
|
|
'discount_policy': 'without_discount',
|
|
'company_id': self.env.company.id,
|
|
'item_ids': [(0, 0, {
|
|
'applied_on': '3_global',
|
|
'compute_price': 'percentage',
|
|
'percent_price': 20,
|
|
})],
|
|
})
|
|
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': self.recurrence_hourly.id,
|
|
'price': 3.5,
|
|
'pricelist_id': pricelist_A.id,
|
|
}, {
|
|
'recurrence_id': self.recurrence_5_hours.id,
|
|
'price': 15.0,
|
|
'pricelist_id': pricelist_B.id,
|
|
}
|
|
]
|
|
self.product_template_id.product_pricing_ids.unlink()
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=self.product_template_id.id)
|
|
pricing = self.env['product.pricing'].create(pricing)
|
|
|
|
reservation_begin = fields.Datetime.now()
|
|
pickup_date = reservation_begin + relativedelta(days=1)
|
|
return_date = pickup_date + relativedelta(hours=1)
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': partner.id,
|
|
'rental_start_date': pickup_date,
|
|
'rental_return_date': return_date,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'product_uom_qty': 1,
|
|
'order_id': sale_order.id,
|
|
'reservation_begin': reservation_begin,
|
|
})
|
|
sol.update({'is_rental': True})
|
|
sale_order.write({'pricelist_id': pricelist_A.id})
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.discount, 0, "Discount should always been 0 on pricelist change")
|
|
sale_order.write({'pricelist_id': pricelist_B.id})
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.discount, 0, "Discount should always been 0 on pricelist change")
|
|
|
|
# TODO availability testing with sale_rental functions? (no stock)
|
|
|
|
def test_no_pickup_nor_return(self):
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
|
|
recurrence_hour = self.env['sale.temporal.recurrence'].create({'duration': 1.0, 'unit': 'hour'})
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': recurrence_hour.id,
|
|
'price': 3.5,
|
|
}
|
|
]
|
|
self.product_template_id.product_pricing_ids.unlink()
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=self.product_template_id.id)
|
|
pricing = self.env['product.pricing'].create(pricing)
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': partner.id,
|
|
})
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertEqual(sol.price_unit, 1, "No pricing should be taken into account if no pickup nor return date.")
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.price_unit, 1, "Update price should not alter first computed price.")
|
|
|
|
def test_no_price_update_on_pickup_return_update(self):
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
|
|
recurrence_hour = self.env['sale.temporal.recurrence'].create({'duration': 1.0, 'unit': 'hour'})
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': recurrence_hour.id,
|
|
'price': 3.5,
|
|
}
|
|
]
|
|
self.product_template_id.product_pricing_ids.unlink()
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=self.product_template_id.id)
|
|
pricing = self.env['product.pricing'].create(pricing)
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': partner.id,
|
|
})
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertEqual(sol.price_unit, 1, "No pricing should be taken into account if no pickup nor return date.")
|
|
sale_order.write({
|
|
'rental_start_date': fields.Datetime.now() + relativedelta(days=1),
|
|
'rental_return_date': fields.Datetime.now() + relativedelta(days=1, hours=1),
|
|
})
|
|
sol.write({'is_rental': True})
|
|
self.assertEqual(sol.price_unit, 1, "Update price should not alter first computed price.")
|
|
sale_order._recompute_prices()
|
|
self.assertEqual(sol.price_unit, 3.5, "Update price should recompute the price.")
|
|
|
|
def test_no_pricing_for_pricelist(self):
|
|
pricelist_A = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist A',
|
|
})
|
|
pricelist_B = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist B',
|
|
})
|
|
partner = self.env['res.partner'].create(
|
|
{'name': 'A partner', 'property_product_pricelist': pricelist_B}
|
|
)
|
|
recurrence_hour = self.env['sale.temporal.recurrence'].create({'duration': 1.0, 'unit': 'hour'})
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': recurrence_hour.id,
|
|
'price': 3.5,
|
|
'pricelist_id': pricelist_A.id,
|
|
}
|
|
]
|
|
self.product_template_id.product_pricing_ids.unlink()
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=self.product_template_id.id)
|
|
self.env['product.pricing'].create(pricing)
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': partner.id,
|
|
'rental_start_date': fields.Datetime.now() + relativedelta(days=1),
|
|
'rental_return_date': fields.Datetime.now() + relativedelta(days=1, hours=1),
|
|
})
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
sol.update({'is_rental': True})
|
|
|
|
self.assertEqual(sol.price_unit, 1, "No pricing should be taken into account if no pricing corresponds to a given pricelist.")
|
|
|
|
def test_contextual_price_is_pickup_return_dependant(self):
|
|
pricelist_A = self.env['product.pricelist'].create({
|
|
'name': 'Pricelist A',
|
|
})
|
|
recurrence_hour = self.env['sale.temporal.recurrence'].create({'duration': 1.0, 'unit': 'hour'})
|
|
PRICINGS = [
|
|
{
|
|
'recurrence_id': recurrence_hour.id,
|
|
'price': 3.5,
|
|
}
|
|
]
|
|
self.product_template_id.product_pricing_ids.unlink()
|
|
for pricing in PRICINGS:
|
|
pricing.update(product_template_id=self.product_template_id.id)
|
|
pricing = self.env['product.pricing'].create(pricing)
|
|
|
|
price = self.product_template_id.with_context(
|
|
start_date=fields.Datetime.now() + relativedelta(days=1),
|
|
end_date=fields.Datetime.now() + relativedelta(days=1, hours=2),
|
|
pricelist=pricelist_A.id
|
|
)._get_contextual_price()
|
|
self.assertEqual(price, 7, "Contextual price should take pickup and return date into account")
|
|
|
|
def test_discount_on_sol_remains(self):
|
|
# Add group 'Discount on Lines' to the user
|
|
self.env.user.write({'groups_id': [(4, self.env.ref('product.group_discount_per_so_line').id)]})
|
|
|
|
now = fields.date.today()
|
|
one_day_later = now + relativedelta(days=1)
|
|
discount = 10
|
|
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': partner.id,
|
|
'rental_start_date': now,
|
|
'rental_return_date': one_day_later,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'product_uom_qty': 1,
|
|
'price_unit': 10,
|
|
'order_id': sale_order.id,
|
|
'reservation_begin': now,
|
|
'discount': discount,
|
|
})
|
|
sol.update({'is_rental': True})
|
|
|
|
self.assertEqual(sol.discount, discount, 'Discount should not be updated')
|
|
sale_order.action_confirm()
|
|
self.assertEqual(sol.discount, discount, 'Discount should not be updated')
|
|
action_dict = sale_order.action_open_pickup()
|
|
pickup_wizard = self.env['rental.order.wizard'].with_context(
|
|
action_dict['context']
|
|
).create({})
|
|
pickup_wizard._get_wizard_lines()
|
|
pickup_wizard.apply()
|
|
self.assertEqual(sol.discount, discount, 'Discount should not be updated')
|
|
|
|
def test_renting_taxes_inc2ex(self):
|
|
|
|
fiscal_position_inc2ex = self.env['account.fiscal.position'].create({'name': 'inc2ex'})
|
|
self.env['account.fiscal.position.tax'].create({
|
|
'position_id': fiscal_position_inc2ex.id,
|
|
'tax_src_id': self.tax_included.id,
|
|
'tax_dest_id': self.tax_excluded.id
|
|
})
|
|
self.product_id.taxes_id = self.tax_included
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'A partner'}).id,
|
|
'fiscal_position_id': fiscal_position_inc2ex.id
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
sol.update({'is_rental': True})
|
|
sale_order._rental_set_dates() # not triggered automatically here
|
|
|
|
self.assertEqual(sale_order.duration_days, 1, 'Default duration should be one day')
|
|
self.assertEqual(sale_order.remaining_hours, 0, 'Default duration should be one day')
|
|
|
|
self.assertTrue(
|
|
float_compare(sol.price_total, 60/1.1, precision_rounding=2),
|
|
"Price with 10% taxes should be equal to basic pricing"
|
|
)
|
|
|
|
def test_renting_taxes_ex2inc(self):
|
|
fiscal_position_ex2inc = self.env['account.fiscal.position'].create({'name': 'ex2inc'})
|
|
self.env['account.fiscal.position.tax'].create({
|
|
'position_id': fiscal_position_ex2inc.id,
|
|
'tax_src_id': self.tax_excluded.id,
|
|
'tax_dest_id': self.tax_included.id
|
|
})
|
|
self.product_id.taxes_id = self.tax_excluded
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'A partner'}).id,
|
|
'fiscal_position_id': fiscal_position_ex2inc.id
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertTrue(
|
|
float_compare(sol.price_unit, 60*1.1, precision_rounding=2),
|
|
"Price with included taxes should be equal to basic pricing(tax excluded) + 10% taxes"
|
|
)
|
|
|
|
def test_renting_taxes_included(self):
|
|
self.product_id.taxes_id = self.tax_included
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'A partner'}).id,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertTrue(
|
|
float_compare(sol.price_unit, 60*1.1, precision_rounding=2),
|
|
"unit price should be equal to basic pricing + 10% (tax included)"
|
|
)
|
|
|
|
def test_renting_taxes_excluded(self):
|
|
self.product_id.taxes_id = self.tax_excluded
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'A partner'}).id,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertTrue(
|
|
float_compare(sol.price_unit, 60, precision_rounding=2),
|
|
"Unit price should be equal to basic pricing (without tax excluded)"
|
|
)
|
|
|
|
def test_renting_taxes_ex_inc(self):
|
|
self.product_id.taxes_id = self.tax_excluded + self.tax_included
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'A partner'}).id,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertTrue(
|
|
float_compare(sol.price_unit, 60*1.1, precision_rounding=2),
|
|
"Price in wizard should be equal to basic pricing + 10% (tax included)"
|
|
)
|
|
|
|
def test_renting_taxes_included_multicompany(self):
|
|
|
|
company2 = self.env['res.company'].create({'name': 'Company 2'})
|
|
self.tax_included.company_id = company2
|
|
|
|
self.product_id.taxes_id = self.tax_included
|
|
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'A partner'}).id,
|
|
})
|
|
|
|
sol = self.env['sale.order.line'].create({
|
|
'product_id': self.product_id.id,
|
|
'order_id': sale_order.id,
|
|
})
|
|
|
|
self.assertTrue(
|
|
float_compare(sol.price_unit, 60, precision_rounding=2),
|
|
"Included tax related to another company should not apply"
|
|
)
|
|
|
|
def test_non_rental_line_does_not_change_description(self):
|
|
water_bottle = self.env["product.product"].create({
|
|
"name": "Water Bottle",
|
|
})
|
|
will_smith = self.env["res.partner"].create({
|
|
"name": "Will Smith",
|
|
})
|
|
|
|
now = fields.Datetime.now()
|
|
order = self.env["sale.order"].create({
|
|
"partner_id": will_smith.id,
|
|
"rental_start_date": now,
|
|
"rental_return_date": now + timedelta(days=2),
|
|
"order_line": [
|
|
Command.create({
|
|
"product_id": self.product_id.id,
|
|
}),
|
|
Command.create({
|
|
"product_id": water_bottle.id
|
|
})
|
|
]
|
|
})
|
|
rental_order_line, non_rental_order_line = order.order_line
|
|
rental_order_line.is_rental = True
|
|
self.assertTrue(order.is_rental_order)
|
|
|
|
non_rental_order_line.name = "My Water Bottle"
|
|
|
|
# Trigger order line name recompute by changing rental field.
|
|
order.rental_start_date = now + timedelta(days=1)
|
|
|
|
# Non-rental order line names aren't recomputed.
|
|
self.assertEqual(non_rental_order_line.name, "My Water Bottle")
|
|
|
|
def test_rental_line_change_description_onchange_duration(self):
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
product = self.env["product.product"].create({
|
|
"name": "Product A",
|
|
})
|
|
|
|
now = fields.Datetime.now()
|
|
order = self.env["sale.order"].create({
|
|
"partner_id": partner.id,
|
|
"rental_start_date": now,
|
|
"rental_return_date": now + timedelta(days=2),
|
|
})
|
|
order_line = self.env['sale.order.line'].create({
|
|
'order_id': order.id,
|
|
'product_id': product.id,
|
|
'product_uom_qty': 1,
|
|
'is_rental': True,
|
|
})
|
|
old_description = order_line.name
|
|
with Form(order) as order_form:
|
|
order_form.rental_start_date = now
|
|
order_form.rental_return_date = now + timedelta(days=3)
|
|
|
|
new_description = order_line.name
|
|
|
|
self.assertNotEqual(
|
|
old_description,
|
|
new_description,
|
|
'order line name for rental order_line should change on change of rental duration on'
|
|
' sale order form'
|
|
)
|
|
|
|
def test_copy_product_variant_pricings(self):
|
|
"""Check that product variants on product pricings after copying
|
|
a product template.
|
|
"""
|
|
product = self.product_template_id.with_context(create_product_product=True)
|
|
product.product_pricing_ids.unlink()
|
|
product_attribute = self.env['product.attribute'].create({
|
|
'name': 'Color',
|
|
'value_ids': [Command.create({'name': name}) for name in ('Blue', 'Red')],
|
|
})
|
|
product.attribute_line_ids = 2 * [Command.create({
|
|
'attribute_id': product_attribute.id,
|
|
'value_ids': product_attribute.value_ids.ids,
|
|
})]
|
|
for i, variant in enumerate(product.product_variant_ids, start=1):
|
|
self.env['product.pricing'].create([{
|
|
'product_template_id': product.id,
|
|
'product_variant_ids': [Command.link(variant.id)],
|
|
'recurrence_id': self.recurrence_hourly.id,
|
|
'price': 10.0 * i,
|
|
'pricelist_id': self.env['product.pricelist'].create({'name': f"list {i}"}).id,
|
|
}, {
|
|
'product_template_id': product.id,
|
|
'product_variant_ids': [Command.link(variant.id)],
|
|
'recurrence_id': self.recurrence_daily.id,
|
|
'price': 25.0 * i,
|
|
}])
|
|
pricings_1 = product.product_pricing_ids.sorted()
|
|
pricings_2 = product.copy().product_pricing_ids.sorted()
|
|
self.assertEqual(
|
|
len(pricings_2),
|
|
8, # 2 attributes * 2 values * 2 plans = 8 pricings
|
|
"copied product should get 8 pricings",
|
|
)
|
|
self.assertNotEqual(
|
|
pricings_2.product_variant_ids,
|
|
pricings_1.product_variant_ids,
|
|
"copied pricings shouldn't be linked to the original products",
|
|
)
|
|
for pricing_1, pricing_2 in zip(pricings_1, pricings_2):
|
|
self.assertEqual(pricing_2.price, pricing_1.price)
|
|
self.assertEqual(pricing_2.recurrence_id, pricing_1.recurrence_id)
|
|
self.assertEqual(pricing_2.pricelist_id, pricing_1.pricelist_id)
|
|
|
|
def test_copy_rental_status(self):
|
|
""" Check that the rental status is duplicated from list view
|
|
"""
|
|
partner = self.env['res.partner'].create({'name': 'A partner'})
|
|
now = self.env.cr.now()
|
|
pickup_date = now + relativedelta(days=1)
|
|
return_date = pickup_date + relativedelta(hours=1)
|
|
|
|
sale_order = self.env['sale.order'].with_context(in_rental_app=True).create({
|
|
'partner_id': partner.id,
|
|
'rental_start_date': pickup_date,
|
|
'rental_return_date': return_date,
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product_id.id,
|
|
'is_rental': True,
|
|
}),
|
|
Command.create({
|
|
'product_id': self.product_id.id,
|
|
'is_rental': False,
|
|
})
|
|
]
|
|
})
|
|
|
|
sol_rent = sale_order.order_line.filtered('is_rental')
|
|
sol_buy = sale_order.order_line - sol_rent
|
|
|
|
dupe_order = sale_order.copy()
|
|
self.assertEqual(dupe_order.rental_status, "draft")
|
|
self.assertEqual(dupe_order.order_line[0].is_rental, sol_rent.is_rental)
|
|
self.assertEqual(dupe_order.order_line[1].is_rental, sol_buy.is_rental)
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestUi(HttpCase):
|
|
|
|
def test_rental_flow(self):
|
|
# somehow, the name_create and onchange of the partner_id
|
|
# in a quotation trigger a re-rendering that loses
|
|
# the focus of some fields, preventing the tour to
|
|
# run successfully if a partner is created during the flow
|
|
# create it in advance here instead
|
|
self.env['res.partner'].name_create('Agrolait')
|
|
self.start_tour("/web", 'rental_tour', login="admin")
|