[IMP] website_sale: Add prevent_zero_price_sale option
After this commit clients will be able to select whether to display the add to cart button when the computed price of a product is 0. This option will be available in the settings. task-2694860 closes odoo/odoo#87952 Related: odoo/enterprise#27594 Signed-off-by: William Braeckman (wbr) <wbr@odoo.com>
This commit is contained in:
committed by
William Braeckman
parent
9765879130
commit
8d22dd8ae9
@@ -12,7 +12,7 @@
|
||||
<img class="o_carousel_product_card_img_top card-img-top p-2" loading="lazy" t-att-src="data['image_512']"
|
||||
t-att-alt="record.display_name"/>
|
||||
</a>
|
||||
<i t-if="data.get('_latest_viewed')" class="fa fa-trash o_carousel_product_remove js_remove"></i>
|
||||
<i t-if="data.get('_latest_viewed')" class="fa fa-trash o_carousel_product_remove js_remove"/>
|
||||
<div class="o_carousel_product_card_body card-body border-top">
|
||||
<a t-att-href="record.website_url" class="text-decoration-none">
|
||||
<div class="h6 card-title mb-0" t-field="record.display_name"/>
|
||||
@@ -287,10 +287,17 @@
|
||||
</template>
|
||||
|
||||
<template id="price_dynamic_filter_template_product_product" name="Dynamic Product Filter Price">
|
||||
<span t-esc="record._get_contextual_price()" class="fw-bold"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<del t-if="data.get('has_discounted_price')" class="text-danger ms-1 h6" style="white-space: nowrap;"
|
||||
t-esc="data['list_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<t t-set="record_price" t-value="record._get_contextual_price()"/>
|
||||
<t t-if="not website.prevent_zero_price_sale or record_price">
|
||||
<span t-esc="record_price" class="fw-bold"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<del t-if="data.get('has_discounted_price')" class="text-danger ms-1 h6" style="white-space: nowrap;"
|
||||
t-esc="data['list_price']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-field="website.prevent_zero_price_sale_text"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Assets -->
|
||||
|
||||
@@ -88,7 +88,8 @@ class Product(models.Model):
|
||||
return combination_info['product_type'] == 'product' and combination_info['free_qty'] <= 0
|
||||
|
||||
def _website_show_quick_add(self):
|
||||
return self.sale_ok
|
||||
website = self.env['website'].get_current_website()
|
||||
return self.sale_ok and (not website.prevent_zero_price_sale or self._get_contextual_price())
|
||||
|
||||
def _is_add_to_cart_allowed(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -255,7 +255,7 @@ class ProductTemplate(models.Model):
|
||||
product, partner
|
||||
)
|
||||
has_discounted_price = pricelist.currency_id.compare_amounts(list_price, price) == 1
|
||||
|
||||
prevent_zero_price_sale = not price and current_website.prevent_zero_price_sale
|
||||
combination_info.update(
|
||||
base_unit_name=product.base_unit_name,
|
||||
base_unit_price=product.base_unit_price,
|
||||
@@ -263,6 +263,7 @@ class ProductTemplate(models.Model):
|
||||
list_price=list_price,
|
||||
price_extra=price_extra,
|
||||
has_discounted_price=has_discounted_price,
|
||||
prevent_zero_price_sale=prevent_zero_price_sale,
|
||||
)
|
||||
|
||||
return combination_info
|
||||
@@ -491,9 +492,13 @@ class ProductTemplate(models.Model):
|
||||
|
||||
def _search_render_results_prices(self, mapping, combination_info):
|
||||
monetary_options = {'display_currency': mapping['detail']['display_currency']}
|
||||
price = self.env['ir.qweb.field.monetary'].value_to_html(
|
||||
combination_info['price'], monetary_options
|
||||
)
|
||||
if combination_info['prevent_zero_price_sale']:
|
||||
website = self.env['website'].get_current_website()
|
||||
price = website.prevent_zero_price_sale_text
|
||||
else:
|
||||
price = self.env['ir.qweb.field.monetary'].value_to_html(
|
||||
combination_info['price'], monetary_options
|
||||
)
|
||||
if combination_info['has_discounted_price']:
|
||||
list_price = self.env['ir.qweb.field.monetary'].value_to_html(
|
||||
combination_info['list_price'], monetary_options
|
||||
@@ -524,4 +529,5 @@ class ProductTemplate(models.Model):
|
||||
return pricelist
|
||||
|
||||
def _website_show_quick_add(self):
|
||||
return self.sale_ok
|
||||
website = self.env['website'].get_current_website()
|
||||
return self.sale_ok and (not website.prevent_zero_price_sale or self._get_contextual_price())
|
||||
|
||||
@@ -59,6 +59,8 @@ class ResConfigSettings(models.TransientModel):
|
||||
compute="_compute_account_on_checkout",
|
||||
inverse="_inverse_account_on_checkout",
|
||||
readonly=False, required=True)
|
||||
website_sale_prevent_zero_price_sale = fields.Boolean(string="Prevent Sale of Zero Priced Product", related='website_id.prevent_zero_price_sale', readonly=False)
|
||||
website_sale_contact_us_button_url = fields.Char(string="Button URL", related='website_id.contact_us_button_url', readonly=False)
|
||||
|
||||
@api.depends('website_id')
|
||||
def _compute_terms_url(self):
|
||||
|
||||
@@ -132,6 +132,9 @@ class SaleOrder(models.Model):
|
||||
if not product or not product._is_add_to_cart_allowed():
|
||||
raise UserError(_("The given product does not exist therefore it cannot be added to cart."))
|
||||
|
||||
if product.lst_price == 0 and product.website_id.prevent_zero_price_sale:
|
||||
raise UserError(_("The given product does not have a price therefore it cannot be added to cart."))
|
||||
|
||||
if line_id is not False:
|
||||
order_line = self._cart_find_product_line(product_id, line_id, **kwargs)[:1]
|
||||
else:
|
||||
|
||||
@@ -101,6 +101,11 @@ class Website(models.Model):
|
||||
], default='small', required=True,
|
||||
)
|
||||
|
||||
prevent_zero_price_sale = fields.Boolean(string="Hide 'Add To Cart' when price = 0")
|
||||
prevent_zero_price_sale_text = fields.Char(string="Text to show instead of price", translate=True,
|
||||
default="Not Available For Sale")
|
||||
contact_us_button_url = fields.Char(string="Contact Us Button URL", translate=True, default="/contactus")
|
||||
|
||||
@api.depends('all_pricelist_ids')
|
||||
def _compute_pricelist_ids(self):
|
||||
for website in self:
|
||||
|
||||
@@ -39,7 +39,24 @@ VariantMixin._onChangeCombination = function (ev, $parent, combination) {
|
||||
$product.data('product-tracking-info', combination['product_tracking_info']);
|
||||
$product.trigger('view_item_event', combination['product_tracking_info']);
|
||||
}
|
||||
|
||||
const addToCart = $parent.find('#add_to_cart_wrap');
|
||||
const contactUsButton = $parent.find('#contact_us_wrapper');
|
||||
const productPrice = $parent.find('.product_price');
|
||||
const quantity = $parent.find('.css_quantity');
|
||||
const product_unavailable = $parent.find('#product_unavailable');
|
||||
if (combination.prevent_zero_price_sale) {
|
||||
productPrice.removeClass('d-inline-block').addClass('d-none');
|
||||
quantity.removeClass('d-inline-flex').addClass('d-none');
|
||||
addToCart.removeClass('d-inline-flex').addClass('d-none');
|
||||
contactUsButton.removeClass('d-none').addClass('d-flex');
|
||||
product_unavailable.removeClass('d-none').addClass('d-flex')
|
||||
} else {
|
||||
productPrice.removeClass('d-none').addClass('d-inline-block');
|
||||
quantity.removeClass('d-none').addClass('d-inline-flex');
|
||||
addToCart.removeClass('d-none').addClass('d-inline-flex');
|
||||
contactUsButton.removeClass('d-flex').addClass('d-none');
|
||||
product_unavailable.removeClass('d-flex').addClass('d-none')
|
||||
}
|
||||
originalOnChangeCombination.apply(this, [ev, $parent, combination]);
|
||||
};
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class TestUi(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
|
||||
product_template = self.env['product.template'].create({
|
||||
'name': 'Dynamic Product',
|
||||
'website_published': True,
|
||||
'list_price': 0,
|
||||
'list_price': 10,
|
||||
})
|
||||
|
||||
# set attribute and attribute values on the template
|
||||
|
||||
@@ -216,6 +216,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="hide_add_to_cart_setting">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="website_sale_prevent_zero_price_sale"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="website_sale_prevent_zero_price_sale"/>
|
||||
<div class="text-muted">
|
||||
If product price equals 0, replace 'Add to Cart' by 'Contact us'.
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': [('website_sale_prevent_zero_price_sale', '=', False)]}">
|
||||
<div class="row mt16">
|
||||
<label class="o_light_label col-lg-3" string="Button url" for="website_sale_contact_us_button_url"/>
|
||||
<field name="website_sale_contact_us_button_url"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Shipping</h2>
|
||||
|
||||
@@ -194,7 +194,8 @@
|
||||
<em class="small" t-esc="template_price_vals['base_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}" />
|
||||
</del>
|
||||
</t>
|
||||
<span class="h6 mb-0" t-if="template_price_vals['price_reduce']" t-esc="template_price_vals['price_reduce']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<span class="h6 mb-0" t-if="template_price_vals['price_reduce'] or not website.prevent_zero_price_sale" t-esc="template_price_vals['price_reduce']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<span class="h6 mb-0" t-else="" t-field="website.prevent_zero_price_sale_text"/>
|
||||
<span itemprop="price" style="display:none;" t-esc="template_price_vals['price_reduce']" />
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="website.currency_id.name" />
|
||||
</div>
|
||||
@@ -215,7 +216,7 @@
|
||||
<xpath expr="//div[hasclass('o_wsale_product_btn')]" position="inside">
|
||||
<t t-set="product_variant_id" t-value="product._get_first_possible_variant_id()"/>
|
||||
<input name="product_id" t-att-value="product_variant_id" type="hidden"/>
|
||||
<t t-if="product_variant_id">
|
||||
<t t-if="product_variant_id and template_price_vals['price_reduce'] or not website.prevent_zero_price_sale">
|
||||
<a t-if="product._website_show_quick_add()"
|
||||
href="#" role="button" class="btn btn-primary a-submit" aria-label="Shopping cart" title="Shopping cart">
|
||||
<span class="fa fa-shopping-cart"/>
|
||||
@@ -649,7 +650,7 @@
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||
<div class="js_product js_main_product mb-3">
|
||||
<div>
|
||||
<t t-call="website_sale.product_price" />
|
||||
<t t-call="website_sale.product_price"/>
|
||||
<small t-if="combination_info['base_unit_price']"
|
||||
class="ms-1 text-muted o_base_unit_price_wrapper d-none" groups="website_sale.group_show_uom_price">
|
||||
<t t-call='website_sale.base_unit_price'/>
|
||||
@@ -674,7 +675,7 @@
|
||||
<t t-set="hasBuyNow" t-value="false"/>
|
||||
<t t-set="ctaSizeBig" t-value="not hasQuantities or not hasBuyNow"/>
|
||||
|
||||
<div id="add_to_cart_wrap" class="d-inline-flex align-items-center mb-2 me-auto">
|
||||
<div id="add_to_cart_wrap" t-attf-class="{{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-flex'}} align-items-center mb-2 me-auto">
|
||||
<a role="button" id="add_to_cart" t-attf-class="btn btn-primary js_check_product a-submit flex-grow-1 #{'btn-lg' if ctaSizeBig else ''}" href="#">
|
||||
<i class="fa fa-shopping-cart me-2"/>
|
||||
<span style="font-weight: bold">ADD TO CART</span>
|
||||
@@ -682,6 +683,16 @@
|
||||
</div>
|
||||
<div id="product_option_block" class="d-flex flex-wrap w-100"/>
|
||||
</div>
|
||||
<div id="contact_us_wrapper"
|
||||
t-attf-class="{{'d-flex' if combination_info['prevent_zero_price_sale'] else 'd-none'}} oe_structure oe_structure_solo #{_div_classes}">
|
||||
<section class="s_text_block" data-snippet="s_text_block" data-name="Text">
|
||||
<div class="container">
|
||||
<a t-att-href="website.contact_us_button_url"
|
||||
class="btn btn-primary btn_cta">Contact Us
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p t-elif="not product.active" class="alert alert-warning">This product is no longer available.</p>
|
||||
@@ -807,7 +818,7 @@
|
||||
<attribute name="t-value" remove="false" add="true" separator=" "/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='add_to_cart_wrap']" position="before">
|
||||
<div t-attf-class="css_quantity input-group d-inline-flex me-2 mb-2 align-middle #{'input-group-lg' if ctaSizeBig else ''}" contenteditable="false">
|
||||
<div t-attf-class="css_quantity input-group {{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-flex'}} me-2 mb-2 align-middle #{'input-group-lg' if ctaSizeBig else ''}" contenteditable="false">
|
||||
<a t-attf-href="#" class="btn btn-link js_add_cart_json" aria-label="Remove one" title="Remove one">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
@@ -832,7 +843,7 @@
|
||||
</template>
|
||||
|
||||
<template id="product_price">
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price d-inline-block mt-2 mb-3">
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" t-attf-class="product_price mt-2 mb-3 {{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-block'}}">
|
||||
<h3 class="css_editable_mode_hidden">
|
||||
<span class="oe_price" style="white-space: nowrap;" t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<span itemprop="price" style="display:none;" t-esc="combination_info['price']"/>
|
||||
@@ -866,6 +877,9 @@
|
||||
</del>
|
||||
</h3>
|
||||
</div>
|
||||
<div id="product_unavailable" t-attf-class="{{'d-flex' if combination_info['prevent_zero_price_sale'] else 'd-none'}}">
|
||||
<h3 class="font-italic" t-field="website.prevent_zero_price_sale_text"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="product_variants" inherit_id="website_sale.product" active="False" name="List View of Variants">
|
||||
|
||||
@@ -113,20 +113,29 @@
|
||||
<span t-esc="combination_info['display_name']"></span><br/>
|
||||
</a>
|
||||
|
||||
<span class="o_comparison_price">
|
||||
<span class="o_comparison_price" t-if="combination_info['prevent_zero_price_sale']">
|
||||
<strong t-field="website.prevent_zero_price_sale_text"/>
|
||||
</span>
|
||||
<span class="o_comparison_price" t-else="">
|
||||
<strong>Price:</strong>
|
||||
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}" style="white-space: nowrap;" t-esc="combination_info['list_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}" />
|
||||
<span t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"></span>
|
||||
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}"
|
||||
style="white-space: nowrap;" t-esc="combination_info['list_price']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<span t-esc="combination_info['price']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<small class="d-block text-muted" groups="website_sale.group_show_uom_price" t-if="combination_info['base_unit_price']">
|
||||
<t t-call='website_sale.base_unit_price'/>
|
||||
</small>
|
||||
</span>
|
||||
|
||||
<form action="/shop/cart/update" method="post" class="text-center">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input name="product_id" t-att-value="product.id" type="hidden"
|
||||
t-att-data-product-tracking-info="json.dumps(request.env['product.template'].get_google_analytics_data(combination_info))"/>
|
||||
<a role="button" class="btn btn-primary d-block a-submit" href="#"><i class="fa fa-shopping-cart me-2"></i>Add to Cart</a>
|
||||
<a t-if="combination_info['prevent_zero_price_sale']" t-att-href="website.contact_us_button_url" class="btn btn-primary btn_cta">Contact Us</a>
|
||||
<a t-else="" role="button" class="btn btn-primary d-block a-submit" href="#">
|
||||
<i class="fa fa-shopping-cart me-2"/>Add to Cart
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
@@ -166,8 +175,11 @@
|
||||
<div class="col-8 ps-2">
|
||||
<h6>
|
||||
<a t-att-href="product.website_url"><t t-esc="combination_info['display_name']" /></a><br/>
|
||||
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}" style="white-space: nowrap;" t-esc="combination_info['list_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}" />
|
||||
<span t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"></span>
|
||||
<div t-attf-class="{{'d-none' if combination_info['prevent_zero_price_sale'] else ''}}">
|
||||
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}" style="white-space: nowrap;" t-esc="combination_info['list_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}" />
|
||||
<span t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
</div>
|
||||
<div t-attf-class="{{'' if combination_info['prevent_zero_price_sale'] else 'd-none'}}" t-field="website.prevent_zero_price_sale_text"/>
|
||||
</h6>
|
||||
</div>
|
||||
<div class="col-1 text-end">
|
||||
|
||||
@@ -188,7 +188,10 @@
|
||||
<small class='d-none d-md-block'><p t-field="wish.product_id.description_sale" class="text-muted"/></small>
|
||||
<button type="button" class="btn btn-link o_wish_rm no-decoration"><small><i class='fa fa-trash-o'></i> Remove</small></button>
|
||||
</td>
|
||||
<td class="align-middle o_wish_price">
|
||||
<td class="align-middle" t-if="combination_info['prevent_zero_price_sale']">
|
||||
<span t-field="website.prevent_zero_price_sale_text"/>
|
||||
</td>
|
||||
<td class="align-middle o_wish_price" t-else="">
|
||||
<t t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
<small t-if="combination_info['base_unit_price']" class="cart_product_base_unit_price d-block text-muted" groups="website_sale.group_show_uom_price">
|
||||
<t t-call='website_sale.base_unit_price'><t t-set='product' t-value='wish.product_id' /></t>
|
||||
@@ -196,7 +199,8 @@
|
||||
</td>
|
||||
<td class='text-center td-wish-btn align-middle'>
|
||||
<input name="product_id" t-att-value="wish.product_id.id" type="hidden"/>
|
||||
<button type="button" role="button" class="btn btn-secondary btn-block o_wish_add mb4" >
|
||||
<a t-if="combination_info['prevent_zero_price_sale']" t-att-href="website.contact_us_button_url" class="btn btn-primary btn_cta">Contact Us</a>
|
||||
<button t-else="" type="button" role="button" class="btn btn-secondary btn-block o_wish_add mb4" >
|
||||
Add <span class='d-none d-md-inline'>to Cart</span>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user