[FIX] website_event_booth_sale, website_event_sale: correct price
Steps to reproduce: - Create a price list with different currency and discount with "show price and discount to the customer" - On the website select this pricelist and register for the event. Issue: The price in the cart is shown in the main currency Solution: [website_event_sale] There is an initial issue which when it calls '_compute_price_reduce'. We compare, 'product.lst_price' (in product.currency) and 'product.price' (which has been converted to the pricelist.currency). In order to compare apples with apples, a conversion is applied to have the 'product.lst_price' in the same currency. After that, we have kind of a coherent behaviour in the sense that 'ticket.price_reduce' is in the same currency as 'ticket.price'. Thereafter, a conversion is applied (if the pricelist.currency is different) to get the expected amount. The same reasoning is applied to [website_event_booth_sale]. Note: 'list_price' has been changed to 'lst_price' in Booth to have the same logic between Event and Booth opw-2766997 X-original-commit: ced49554dd7cca73a1da440607d545bad64e7af0 Part-of: odoo/odoo#107768
This commit is contained in:
@@ -49,8 +49,14 @@ class EventBoothCategory(models.Model):
|
||||
def _compute_price_reduce(self):
|
||||
for category in self:
|
||||
product = category.product_id
|
||||
list_price = product.list_price + product.price_extra
|
||||
discount = (list_price - product._get_contextual_price()) / list_price if list_price else 0.0
|
||||
pricelist = product.product_tmpl_id._get_contextual_pricelist()
|
||||
lst_price = product.currency_id._convert(
|
||||
product.lst_price,
|
||||
pricelist.currency_id,
|
||||
self.env.company,
|
||||
fields.Datetime.now()
|
||||
)
|
||||
discount = (lst_price - product._get_contextual_price()) / lst_price if lst_price else 0.0
|
||||
category.price_reduce = (1.0 - discount) * category.price
|
||||
|
||||
@api.depends_context('pricelist', 'quantity')
|
||||
|
||||
@@ -94,7 +94,12 @@ class SaleOrderLine(models.Model):
|
||||
if self.event_booth_pending_ids and self.event_id:
|
||||
company = self.event_id.company_id or self.env.company
|
||||
currency = company.currency_id
|
||||
total_price = sum([booth.price for booth in self.event_booth_pending_ids])
|
||||
product = self.event_booth_pending_ids.booth_category_id.product_id
|
||||
pricelist = product.product_tmpl_id._get_contextual_pricelist()
|
||||
if pricelist.discount_policy == "with_discount":
|
||||
total_price = sum([booth.booth_category_id.price_reduce for booth in self.event_booth_pending_ids])
|
||||
else:
|
||||
total_price = sum([booth.price for booth in self.event_booth_pending_ids])
|
||||
return currency._convert(
|
||||
total_price, self.order_id.currency_id,
|
||||
self.order_id.company_id or self.env.company.id,
|
||||
|
||||
@@ -54,10 +54,14 @@ class EventTemplateTicket(models.Model):
|
||||
def _compute_price_reduce(self):
|
||||
for ticket in self:
|
||||
product = ticket.product_id
|
||||
# seems strange to not apply pricelist logic but still use pricelist discount...
|
||||
discount = (
|
||||
product.lst_price - product._get_contextual_price()
|
||||
) / product.lst_price if product.lst_price else 0.0
|
||||
pricelist = product.product_tmpl_id._get_contextual_pricelist()
|
||||
lst_price = product.currency_id._convert(
|
||||
product.lst_price,
|
||||
pricelist.currency_id,
|
||||
self.env.company,
|
||||
fields.Datetime.now()
|
||||
)
|
||||
discount = (lst_price - product._get_contextual_price()) / lst_price if lst_price else 0.0
|
||||
ticket.price_reduce = (1.0 - discount) * ticket.price
|
||||
|
||||
def _init_column(self, column_name):
|
||||
|
||||
@@ -172,9 +172,15 @@ class SaleOrderLine(models.Model):
|
||||
pricelist=self.order_id.pricelist_id.id,
|
||||
uom=self.product_uom.id
|
||||
)
|
||||
if self.order_id.pricelist_id.discount_policy == 'with_discount':
|
||||
return event_ticket.price_reduce
|
||||
company = event_ticket.company_id or self.env.company
|
||||
currency = company.currency_id
|
||||
pricelist = self.order_id.pricelist_id
|
||||
if pricelist.discount_policy == "with_discount":
|
||||
price = event_ticket.price_reduce
|
||||
else:
|
||||
return event_ticket.price
|
||||
else:
|
||||
return super()._get_display_price()
|
||||
price = event_ticket.price
|
||||
return currency._convert(
|
||||
price, self.order_id.currency_id,
|
||||
self.order_id.company_id or self.env.company.id,
|
||||
self.order_id.date_order or fields.Date.today())
|
||||
return super()._get_display_price()
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_event_booth_sale
|
||||
from . import test_website_event_booth_sale_pricelist
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.event_booth_sale.tests.common import TestEventBoothSaleCommon
|
||||
from odoo.addons.website_event_sale.tests.common import TestWebsiteEventSaleCommon
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteBoothPriceList(TestEventBoothSaleCommon, TestWebsiteEventSaleCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestWebsiteBoothPriceList, cls).setUpClass()
|
||||
|
||||
cls.WebsiteSaleController = WebsiteSale()
|
||||
cls.booth_1 = cls.env['event.booth'].create({
|
||||
'booth_category_id': cls.event_booth_category_1.id,
|
||||
'event_id': cls.event.id,
|
||||
'name': 'Test Booth 1',
|
||||
})
|
||||
|
||||
cls.booth_2 = cls.env['event.booth'].create({
|
||||
'booth_category_id': cls.event_booth_category_1.id,
|
||||
'event_id': cls.event.id,
|
||||
'name': 'Test Booth 2',
|
||||
})
|
||||
|
||||
def test_pricelist_different_currency(self):
|
||||
so_line = self.env['sale.order.line'].create({
|
||||
'event_booth_category_id': self.event_booth_category_1.id,
|
||||
'event_booth_pending_ids': (self.booth_1 + self.booth_2).ids,
|
||||
'event_id': self.event.id,
|
||||
'order_id': self.so.id,
|
||||
'product_id': self.event_booth_product.id,
|
||||
})
|
||||
# set pricelist to 0 - currency: company
|
||||
self.pricelist.write({
|
||||
'currency_id': self.new_company.currency_id.id,
|
||||
'discount_policy': 'with_discount',
|
||||
'item_ids': [(5, 0, 0), (0, 0, {
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': 0,
|
||||
})],
|
||||
'name': 'With Discount Included',
|
||||
})
|
||||
with MockRequest(self.env, sale_order_id=self.so.id, website=self.current_website):
|
||||
self.WebsiteSaleController.pricelist(promo=None)
|
||||
self.so._cart_update(line_id=so_line.id, product_id=self.event_booth_product.id, set_qty=1)
|
||||
self.assertEqual(so_line.price_reduce, 40)
|
||||
|
||||
# set pricelist to 10% - without discount
|
||||
self.pricelist.write({
|
||||
'currency_id': self.currency_test.id,
|
||||
'discount_policy': 'without_discount',
|
||||
'item_ids': [(5, 0, 0), (0, 0, {
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': 10,
|
||||
})],
|
||||
'name': 'Without Discount Included',
|
||||
})
|
||||
with MockRequest(self.env, sale_order_id=self.so.id, website=self.current_website):
|
||||
self.WebsiteSaleController.pricelist(promo=None)
|
||||
self.so._cart_update(line_id=so_line.id, product_id=self.event_booth_product.id, set_qty=1)
|
||||
self.assertEqual(so_line.price_reduce, 360, 'Incorrect amount based on the pricelist "Without Discount" and its currency.')
|
||||
|
||||
# set pricelist to 10% - with discount
|
||||
self.pricelist.write({
|
||||
'discount_policy': 'with_discount',
|
||||
'name': 'With Discount Included',
|
||||
})
|
||||
with MockRequest(self.env, sale_order_id=self.so.id, website=self.current_website):
|
||||
self.WebsiteSaleController.pricelist(promo=None)
|
||||
self.so._cart_update(line_id=so_line.id, product_id=self.event_booth_product.id, set_qty=1)
|
||||
self.assertEqual(so_line.price_reduce, 360, 'Incorrect amount based on the pricelist "With Discount" and its currency.')
|
||||
@@ -4,19 +4,29 @@
|
||||
<template id="event_booth_registration" inherit_id="website_event_booth.event_booth_registration">
|
||||
<xpath expr="//h5[@name='booth_category_name']" position="after">
|
||||
<t t-if="booth_category.price">
|
||||
<t t-if="(booth_category.price - booth_category.price_reduce) > 1 and website.get_current_pricelist().discount_policy == 'without_discount'">
|
||||
<del class="text-danger me-1"
|
||||
t-field="booth_category.price"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.get_current_pricelist().currency_id}"/>
|
||||
</t>
|
||||
<span t-field="booth_category.price_reduce" class="fw-normal text-muted"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"
|
||||
groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||
<span t-field="booth_category.price_reduce_taxinc" class="fw-normal text-muted"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"
|
||||
groups="account.group_show_line_subtotals_tax_included"/>
|
||||
</t>
|
||||
<span t-else="" class="fw-normal text-muted">Free</span>
|
||||
</xpath>
|
||||
<xpath expr="//input[@name='booth_category_id']" position="attributes">
|
||||
<attribute name="t-att-data-price">
|
||||
(booth_category.price_reduce_taxinc if env.user.has_group('account.group_show_line_subtotals_tax_included')
|
||||
else booth_category.price_reduce) or '0'</attribute>
|
||||
event.company_id.sudo().currency_id._convert(
|
||||
booth_category.price_reduce_taxeinc if env.user.has_group('account.group_show_line_subtotals_tax_included') else booth_category.price_reduce,
|
||||
website.get_current_pricelist().currency_id,
|
||||
event.company_id,
|
||||
datetime.date.today()
|
||||
) or '0'
|
||||
</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='booth_registration_submit']" position="before">
|
||||
<div class="row o_wbooth_booth_total_price d-none">
|
||||
@@ -25,7 +35,7 @@
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<span class="fw-bold" t-out="float(0)"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import common
|
||||
from . import test_frontend_buy_tickets
|
||||
from . import test_website_event_sale_pricelist
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo.fields import Datetime
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestWebsiteEventSaleCommon(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestWebsiteEventSaleCommon, cls).setUpClass()
|
||||
|
||||
cls.env.ref('base.USD').write({'active': False})
|
||||
cls.currency_test = cls.env['res.currency'].create({
|
||||
'name': 'eventX',
|
||||
'rate': 10,
|
||||
'rounding': 0.01,
|
||||
'symbol': 'EX',
|
||||
})
|
||||
|
||||
cls.partner = cls.env['res.partner'].create({'name': 'test'})
|
||||
cls.new_company = cls.env['res.company'].create({
|
||||
'currency_id': cls.env.ref('base.EUR').id,
|
||||
'name': 'Great Company EUR',
|
||||
'partner_id': cls.partner.id,
|
||||
})
|
||||
cls.env['res.currency.rate'].create({
|
||||
'company_id': cls.new_company.id,
|
||||
'currency_id': cls.currency_test.id,
|
||||
'name': '2022-01-01',
|
||||
'rate': 10,
|
||||
})
|
||||
|
||||
cls.product_event = cls.env['product.product'].create({
|
||||
'company_id': cls.new_company.id,
|
||||
'currency_id': cls.env.ref('base.EUR').id,
|
||||
'detailed_type': 'event',
|
||||
'list_price': 100,
|
||||
'name': 'Event Registration No Company Assigned',
|
||||
})
|
||||
|
||||
cls.event = cls.env['event.event'].create({
|
||||
'date_begin': (Datetime.today() + timedelta(days=5)).strftime('%Y-%m-%d 07:00:00'),
|
||||
'date_end': (Datetime.today() + timedelta(days=5)).strftime('%Y-%m-%d 16:30:00'),
|
||||
'name': 'Pycon',
|
||||
'user_id': cls.env.ref('base.user_admin').id,
|
||||
'website_published': True,
|
||||
})
|
||||
cls.ticket = cls.env['event.event.ticket'].create([{
|
||||
'event_id': cls.event.id,
|
||||
'name': 'Standard',
|
||||
'product_id': cls.product_event.id,
|
||||
'price': 100,
|
||||
}])
|
||||
|
||||
cls.current_website = cls.env['website'].get_current_website()
|
||||
cls.current_website.company_id = cls.new_company
|
||||
cls.pricelist = cls.current_website.get_current_pricelist()
|
||||
|
||||
cls.so = cls.env['sale.order'].create({
|
||||
'company_id': cls.new_company.id,
|
||||
'partner_id': cls.partner.id,
|
||||
'pricelist_id': cls.pricelist.id,
|
||||
})
|
||||
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
from odoo.addons.website_event_sale.tests.common import TestWebsiteEventSaleCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteEventPriceList(TestWebsiteEventSaleCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestWebsiteEventPriceList, cls).setUpClass()
|
||||
|
||||
cls.WebsiteSaleController = WebsiteSale()
|
||||
|
||||
def test_pricelist_different_currency(self):
|
||||
|
||||
so_line = self.env['sale.order.line'].create({
|
||||
'event_id': self.event.id,
|
||||
'event_ticket_id': self.ticket.id,
|
||||
'name': self.event.name,
|
||||
'order_id': self.so.id,
|
||||
'product_id': self.ticket.product_id.id,
|
||||
'product_uom_qty': 1,
|
||||
})
|
||||
# set pricelist to 0 - currency: company
|
||||
self.pricelist.write({
|
||||
'currency_id': self.new_company.currency_id.id,
|
||||
'discount_policy': 'with_discount',
|
||||
'item_ids': [(5, 0, 0), (0, 0, {
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': 0,
|
||||
})],
|
||||
'name': 'With Discount Included',
|
||||
})
|
||||
with MockRequest(self.env, sale_order_id=self.so.id, website=self.current_website):
|
||||
self.WebsiteSaleController.pricelist(promo=None)
|
||||
self.so._cart_update(line_id=so_line.id, product_id=self.ticket.product_id.id, set_qty=1)
|
||||
self.assertEqual(so_line.price_reduce, 100)
|
||||
|
||||
# set pricelist to 10% - without discount
|
||||
self.pricelist.write({
|
||||
'currency_id': self.currency_test.id,
|
||||
'discount_policy': 'without_discount',
|
||||
'item_ids': [(5, 0, 0), (0, 0, {
|
||||
'applied_on': '3_global',
|
||||
'compute_price': 'percentage',
|
||||
'percent_price': 10,
|
||||
})],
|
||||
'name': 'Without Discount Included',
|
||||
})
|
||||
with MockRequest(self.env, sale_order_id=self.so.id, website=self.current_website):
|
||||
self.WebsiteSaleController.pricelist(promo=None)
|
||||
self.so._cart_update(line_id=so_line.id, product_id=self.ticket.product_id.id, set_qty=1)
|
||||
self.assertEqual(so_line.price_reduce, 900, 'Incorrect amount based on the pricelist and its currency.')
|
||||
|
||||
# set pricelist to 10% - with discount
|
||||
self.pricelist.write({
|
||||
'discount_policy': 'with_discount',
|
||||
'name': 'With Discount Included',
|
||||
})
|
||||
with MockRequest(self.env, sale_order_id=self.so.id, website=self.current_website):
|
||||
self.WebsiteSaleController.pricelist(promo=None)
|
||||
self.so._cart_update(line_id=so_line.id, product_id=self.ticket.product_id.id, set_qty=1)
|
||||
self.assertEqual(so_line.price_reduce, 900, 'Incorrect amount based on the pricelist and its currency.')
|
||||
@@ -5,11 +5,15 @@
|
||||
<!-- Add price information on tickets (multi tickets, aka in collapse) -->
|
||||
<xpath expr="//div[hasclass('o_wevent_registration_multi_select')]" position="inside">
|
||||
<t t-if="ticket.price">
|
||||
<t t-if="(ticket.price-website.currency_id._convert(ticket.price_reduce, event.company_id.sudo().currency_id, event.company_id, datetime.date.today())) > 1 and website.pricelist_id.discount_policy == 'without_discount'">
|
||||
<t t-if="(ticket.price - ticket.price_reduce) > 1 and website.pricelist_id.discount_policy == 'without_discount'">
|
||||
<del class="text-danger me-1" t-field="ticket.price" t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
</t>
|
||||
<span t-field="ticket.price_reduce" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}" groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||
<span t-field="ticket.price_reduce_taxinc" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}" groups="account.group_show_line_subtotals_tax_included"/>
|
||||
<span t-field="ticket.price_reduce"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"
|
||||
groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||
<span t-field="ticket.price_reduce_taxinc"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"
|
||||
groups="account.group_show_line_subtotals_tax_included"/>
|
||||
<span itemprop="price" class="d-none" t-out="ticket.price"/>
|
||||
<span itemprop="priceCurrency" class="d-none" t-out="website.pricelist_id.currency_id.name"/>
|
||||
</t>
|
||||
@@ -19,10 +23,10 @@
|
||||
<t t-if="event.event_ticket_ids[-1].price_reduce > 0">
|
||||
<span class="text-dark">
|
||||
From
|
||||
<span t-out="event.event_ticket_ids[0].price_reduce" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
<span t-out="event.event_ticket_ids[0].price_reduce" t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
<t t-if="event.event_ticket_ids[-1].price_reduce != event.event_ticket_ids[0].price_reduce">
|
||||
to
|
||||
<span t-out="event.event_ticket_ids[-1].price_reduce" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
<span t-out="event.event_ticket_ids[-1].price_reduce" t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
@@ -31,11 +35,15 @@
|
||||
<xpath expr="//div[hasclass('o_wevent_registration_single')]//h6" position="after">
|
||||
<div class="px-2 text-dark d-flex align-items-center align-self-stretch">
|
||||
<t t-if="tickets.price">
|
||||
<t t-if="(tickets.price-website.currency_id._convert(tickets.price_reduce, event.company_id.sudo().currency_id, event.company_id, datetime.date.today())) > 1 and website.pricelist_id.discount_policy == 'without_discount'">
|
||||
<t t-if="(tickets.price - tickets.price_reduce) >1 and website.get_current_pricelist().discount_policy == 'without_discount'">
|
||||
<del class="text-danger me-1" t-field="tickets.price" t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
</t>
|
||||
<span t-field="tickets.price_reduce" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}" groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||
<span t-field="tickets.price_reduce_taxinc" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}" groups="account.group_show_line_subtotals_tax_included"/>
|
||||
<span t-field="tickets.price_reduce"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"
|
||||
groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||
<span t-field="tickets.price_reduce_taxinc"
|
||||
t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.pricelist_id.currency_id}"
|
||||
groups="account.group_show_line_subtotals_tax_included"/>
|
||||
<span itemprop="price" class="d-none" t-out="tickets.price"/>
|
||||
<span itemprop="priceCurrency" class="d-none" t-out="website.pricelist_id.currency_id.name"/>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user