[FIX] website_sale_slides: fix sync between product and course
Before, each time we created a course "on payment". The product linked to it was automatically unpublished. If multiple course share the same product this leads to issues. Indeed, with the product unpublished all the courses depending on the product are no longer buyable. To avoid this, we synchronise the published value when the course is published which published the product. If we create a new course or unpublished a previous course linked to a product, we don't update the published value of the product unless all courses linked to this product are unpublished. We also fix a Forbidden error raised when the product was unpublished but not the course. This was leading to a previous member to not have access to a course he previously buyed. task-2842624 Part-of: odoo/odoo#90736
This commit is contained in:
@@ -21,10 +21,15 @@ class WebsiteSaleSlides(WebsiteSlides):
|
||||
def _prepare_additional_channel_values(self, values, **kwargs):
|
||||
values = super(WebsiteSaleSlides, self)._prepare_additional_channel_values(values, **kwargs)
|
||||
channel = values['channel']
|
||||
if channel.enroll == 'payment' and channel.product_id:
|
||||
pricelist = request.website.get_current_pricelist()
|
||||
values['product_info'] = channel.product_id.product_tmpl_id._get_combination_info(product_id=channel.product_id.id, pricelist=pricelist)
|
||||
values['product_info']['currency_id'] = request.website.currency_id
|
||||
if channel.enroll == 'payment':
|
||||
# search the product to apply ACLs, notably on published status, to avoid access errors
|
||||
product = request.env['product.product'].search([('id', '=', channel.product_id.id)]) if channel.product_id else request.env['product.product']
|
||||
if product:
|
||||
pricelist = request.website.get_current_pricelist()
|
||||
values['product_info'] = channel.product_id.product_tmpl_id._get_combination_info(product_id=channel.product_id.id, pricelist=pricelist)
|
||||
values['product_info']['currency_id'] = request.website.currency_id
|
||||
else:
|
||||
values['product_info'] = False
|
||||
return values
|
||||
|
||||
def _slide_channel_prepare_values(self, **kwargs):
|
||||
|
||||
@@ -52,10 +52,24 @@ class Channel(models.Model):
|
||||
return res
|
||||
|
||||
def _synchronize_product_publish(self):
|
||||
"""
|
||||
Ensure that when publishing a course that its linked product is also published
|
||||
If all courses linked to a product are unpublished, we also unpublished the product
|
||||
"""
|
||||
if not self:
|
||||
return
|
||||
self.filtered(lambda channel: channel.is_published and not channel.product_id.is_published).sudo().product_id.write({'is_published': True})
|
||||
self.filtered(lambda channel: not channel.is_published and channel.product_id.is_published).sudo().product_id.write({'is_published': False})
|
||||
|
||||
unpublished_channel_products = self.filtered(lambda channel: not channel.is_published).product_id
|
||||
group_data = self.read_group(
|
||||
[('is_published', '=', True), ('product_id', 'in', unpublished_channel_products.ids)],
|
||||
['product_id'],
|
||||
['product_id'],
|
||||
)
|
||||
used_product_ids = [product['product_id'][0] for product in group_data if product['product_id_count'] > 0]
|
||||
product_to_unpublish = unpublished_channel_products.filtered(lambda product: product.id not in used_product_ids)
|
||||
if product_to_unpublish:
|
||||
product_to_unpublish.sudo().write({'is_published': False})
|
||||
|
||||
def action_view_sales(self):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("website_sale_slides.sale_report_action_slides")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.website_slides.tests import common
|
||||
from odoo.tests.common import users
|
||||
|
||||
|
||||
class TestCoursePurchaseFlow(common.SlidesCase):
|
||||
@@ -15,9 +16,7 @@ class TestCoursePurchaseFlow(common.SlidesCase):
|
||||
'groups_id': [(6, 0, cls.env.ref('sales_team.group_sale_salesman').ids)],
|
||||
})
|
||||
|
||||
def test_course_purchase_flow(self):
|
||||
# Step1: create a course product and assign it to 2 slide.channels
|
||||
course_product = self.env['product.product'].create({
|
||||
cls.course_product = cls.env['product.product'].create({
|
||||
'name': "Course Product",
|
||||
'standard_price': 100,
|
||||
'list_price': 150,
|
||||
@@ -26,15 +25,17 @@ class TestCoursePurchaseFlow(common.SlidesCase):
|
||||
'is_published': True,
|
||||
})
|
||||
|
||||
def test_course_purchase_flow(self):
|
||||
# Step1: assign a course product to 2 slide.channels
|
||||
self.channel.write({
|
||||
'enroll': 'payment',
|
||||
'product_id': course_product.id
|
||||
'product_id': self.course_product.id
|
||||
})
|
||||
|
||||
self.channel_2 = self.env['slide.channel'].with_user(self.user_officer).create({
|
||||
'name': 'Test Channel',
|
||||
'enroll': 'payment',
|
||||
'product_id': course_product.id,
|
||||
'product_id': self.course_product.id,
|
||||
'is_published': True,
|
||||
})
|
||||
|
||||
@@ -43,10 +44,10 @@ class TestCoursePurchaseFlow(common.SlidesCase):
|
||||
'partner_id': self.customer.id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': course_product.name,
|
||||
'product_id': course_product.id,
|
||||
'name': self.course_product.name,
|
||||
'product_id': self.course_product.id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': course_product.list_price,
|
||||
'price_unit': self.course_product.list_price,
|
||||
})
|
||||
],
|
||||
})
|
||||
@@ -62,10 +63,10 @@ class TestCoursePurchaseFlow(common.SlidesCase):
|
||||
'partner_id': self.user_portal.partner_id.id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': course_product.name,
|
||||
'product_id': course_product.id,
|
||||
'name': self.course_product.name,
|
||||
'product_id': self.course_product.id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': course_product.list_price,
|
||||
'price_unit': self.course_product.list_price,
|
||||
})
|
||||
],
|
||||
})
|
||||
@@ -74,3 +75,52 @@ class TestCoursePurchaseFlow(common.SlidesCase):
|
||||
|
||||
self.assertIn(self.user_portal.partner_id, self.channel.partner_ids)
|
||||
self.assertIn(self.user_portal.partner_id, self.channel_2.partner_ids)
|
||||
|
||||
@users('user_officer')
|
||||
def test_course_product_published_synch(self):
|
||||
""" Test the synchronization between a course and its product """
|
||||
course_1 = self.env['slide.channel'].create({
|
||||
'name': 'Test Channel 1',
|
||||
'enroll': 'payment',
|
||||
'product_id': self.course_product.id,
|
||||
})
|
||||
course_2 = self.env['slide.channel'].create({
|
||||
'name': 'Test Channel 2',
|
||||
'enroll': 'payment',
|
||||
'product_id': self.course_product.id,
|
||||
'is_published': True,
|
||||
})
|
||||
|
||||
# The course_1 is not published by default which doesn't impact the product
|
||||
self.assertFalse(course_1.is_published)
|
||||
self.assertTrue(self.course_product.is_published)
|
||||
|
||||
course_1.is_published = True
|
||||
|
||||
# The course_1 and the product are published
|
||||
self.assertTrue(course_1.is_published)
|
||||
self.assertTrue(self.course_product.is_published)
|
||||
|
||||
self.course_product.is_published = False
|
||||
|
||||
# Unpublishing the product should not change the course_1
|
||||
self.assertTrue(course_1.is_published)
|
||||
self.assertFalse(self.course_product.is_published)
|
||||
|
||||
course_1.is_published = False
|
||||
|
||||
self.assertFalse(course_1.is_published)
|
||||
self.assertFalse(self.course_product.is_published)
|
||||
|
||||
course_1.is_published = True
|
||||
|
||||
# Publishing the course_1 should publish the product
|
||||
self.assertTrue(course_1.is_published)
|
||||
self.assertTrue(self.course_product.is_published)
|
||||
|
||||
(course_1 + course_2).write({'is_published': False})
|
||||
|
||||
# If all course linked to a product are unpublished, we unpublished the product
|
||||
self.assertFalse(course_1.is_published)
|
||||
self.assertFalse(course_2.is_published)
|
||||
self.assertFalse(self.course_product.is_published)
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</template>
|
||||
|
||||
<template name="Buy Course Button" id="course_buy_course_button">
|
||||
<t t-if="channel.product_id.website_published and not channel.is_member">
|
||||
<t t-if="product_info and channel.product_id.website_published and not channel.is_member">
|
||||
<div t-attf-class="text-center d-flex align-items-center text-center pb-1 #{'justify-content-between' if product_info['has_discounted_price'] else 'justify-content-around'}">
|
||||
<div class="css_editable_mode_hidden">
|
||||
<!-- real price -->
|
||||
|
||||
Reference in New Issue
Block a user