mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-26 14:12:04 +02:00
4462 lines
234 KiB
XML
4462 lines
234 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<template id="header_cart_link" name="Header Cart Link">
|
|
<t t-set="website_sale_cart_quantity" t-value="request.session.get('website_sale_cart_quantity', request.cart.cart_quantity) or 0"/>
|
|
<t t-set="show_cart" t-value="website.has_ecommerce_access()"/>
|
|
<li t-attf-class="#{_item_class} divider d-none"/> <!-- Make sure the cart and related menus are not folded (see autohideMenu) -->
|
|
<li t-attf-class="o_wsale_my_cart #{not show_cart and 'd-none'} #{_item_class}">
|
|
<a href="/shop/cart" t-attf-class="#{_link_class}" aria-label="eCommerce cart">
|
|
<div t-attf-class="#{_icon_wrap_class}">
|
|
<i t-if="_icon" class="fa fa-shopping-cart fa-stack"/>
|
|
<sup t-attf-class="my_cart_quantity badge bg-primary #{_badge_class} #{'d-none' if (website_sale_cart_quantity == 0) else ''}" t-out="website_sale_cart_quantity" t-att-data-order-id="request.session.get('sale_order_id', None) if website_sale_cart_quantity else None"/>
|
|
</div>
|
|
<span t-if="_text" t-attf-class="#{_text_class}">My Cart</span>
|
|
</a>
|
|
</li>
|
|
</template>
|
|
|
|
<template id="header_hide_empty_cart_link" inherit_id="website_sale.header_cart_link" name="Header Hide Empty Cart link" active="False">
|
|
<xpath expr="//t[@t-set='show_cart']" position="after">
|
|
<t t-set="show_cart" t-value="website_sale_cart_quantity" />
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Insert JSON-LD markup data for SEO. -->
|
|
<template id="website_sale_layout" inherit_id="website.layout" priority="1">
|
|
<body position="before">
|
|
<t
|
|
t-if="website._get_product_image_ratio() != '1_1'"
|
|
t-set="body_classname"
|
|
t-value="'o_wsale_products_opt_thumb_' + website._get_product_image_ratio() + (' ' + body_classname if body_classname else '')"
|
|
/>
|
|
</body>
|
|
<head position="inside">
|
|
<!-- Company markup data, in all pages. -->
|
|
<t t-set="base_url" t-value="website.get_base_url()"/>
|
|
<script t-if="res_company and website" type="application/ld+json" >
|
|
{
|
|
"@context": "http://schema.org",
|
|
"@type": "Organization",
|
|
"name": "<t t-out="res_company.name"/>",
|
|
"logo": "<t t-out="'%s/logo.png?company=%s' % (base_url, res_company.id)"/>",
|
|
"url": "<t t-out="base_url"/>"
|
|
}
|
|
</script>
|
|
|
|
<!-- Product markup data, on product pages. -->
|
|
<script
|
|
t-if="product_markup_data" type="application/ld+json" t-out="product_markup_data"
|
|
/>
|
|
</head>
|
|
</template>
|
|
|
|
<template
|
|
id="template_footer_website_sale"
|
|
name="eCommerce Footer"
|
|
inherit_id="website.layout"
|
|
active="False"
|
|
>
|
|
<div id="footer" position="replace">
|
|
<div
|
|
t-if="not no_footer"
|
|
id="footer"
|
|
class="oe_structure oe_structure_solo"
|
|
t-ignore="true"
|
|
>
|
|
<section class="s_text_block pt48" data-snippet="s_text_block" data-name="Container">
|
|
<div class="container s_allow_columns">
|
|
<div class="row">
|
|
<!-- Column 1: Products -->
|
|
<div class="col-lg-3 pb32">
|
|
<h5>Products</h5>
|
|
<div class="s_hr pb8" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto"/>
|
|
</div>
|
|
<ul class="list-unstyled">
|
|
<t t-set="ecommerce_categories" t-value="[]"/>
|
|
<li t-foreach="ecommerce_categories" t-as="category">
|
|
<a
|
|
t-att-href="'/shop/category/' + str(category['id'])"
|
|
t-out="category['name']"
|
|
/>
|
|
</li>
|
|
<t t-if="not ecommerce_categories">
|
|
<li><a href="#">Accessories</a></li>
|
|
<li><a href="#">Apparel</a></li>
|
|
<li><a href="#">Beauty</a></li>
|
|
<li><a href="#">Essentials</a></li>
|
|
<li><a href="#">Fitness</a></li>
|
|
<li><a href="#">Gadgets</a></li>
|
|
</t>
|
|
</ul>
|
|
<div class="s_hr pb8" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto"/>
|
|
</div>
|
|
<a href="/shop"><i class="fa fa-long-arrow-right"/> All Products</a>
|
|
<div class="s_hr pt8 pb8" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto"/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Column 2: Customer Care -->
|
|
<div class="col-lg-3 pb32">
|
|
<h5>Customer Care</h5>
|
|
<div class="s_hr pb8" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto"/>
|
|
</div>
|
|
<ul class="list-unstyled">
|
|
<li><a href="#">Help Center</a></li>
|
|
<li><a href="#">Returns</a></li>
|
|
<li><a href="#">Delivery</a></li>
|
|
<li><a href="#">Shipping</a></li>
|
|
<li><a href="/contactus">Contact us</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Column 3: Badges, Social, Payments -->
|
|
<div class="col-lg-5 offset-lg-1 pb48">
|
|
<!-- Payment Icons -->
|
|
<h5>Our payment methods</h5>
|
|
<div class="s_hr pb8" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto"/>
|
|
</div>
|
|
<div
|
|
class="s_supported_payment_methods o_not_editable"
|
|
data-snippet="s_supported_payment_methods"
|
|
data-name="Supported Payment Methods"
|
|
data-limit="8"
|
|
data-height="32px"
|
|
data-oe-protected="true"
|
|
/>
|
|
<div class="s_hr pt8 pb24" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto"/>
|
|
</div>
|
|
<!-- Service Badges -->
|
|
<span
|
|
class="s_badge badge text-bg-primary o_animable"
|
|
data-name="Badge"
|
|
>
|
|
<i class="fa fa-dollar fa-fw o_not-animable"/> Low Prices
|
|
</span>
|
|
<span
|
|
class="s_badge badge text-bg-primary o_animable"
|
|
data-name="Badge"
|
|
>
|
|
<i class="fa fa-shopping-basket fa-fw o_not-animable"/>
|
|
Easy Returns
|
|
</span>
|
|
<span
|
|
class="s_badge badge text-bg-primary o_animable"
|
|
data-name="Badge"
|
|
>
|
|
<i class="fa fa-truck fa-fw o_not-animable"/> Fast Shipping
|
|
</span>
|
|
<div class="s_hr pt8 pb8" data-snippet="s_hr" data-name="Separator">
|
|
<hr class="w-100 mx-auto" style="border-top-color: rgba(255, 255, 255, 0);"/>
|
|
</div>
|
|
<!-- Social Media -->
|
|
<div
|
|
class="s_social_media text-start o_not_editable"
|
|
data-snippet="s_social_media"
|
|
data-name="Social Media"
|
|
contenteditable="false"
|
|
>
|
|
<h5 class="s_social_media_title d-none">
|
|
Follow us
|
|
</h5>
|
|
<a
|
|
href="/website/social/instagram"
|
|
class="s_social_media_instagram"
|
|
target="_blank"
|
|
aria-label="Instagram"
|
|
>
|
|
<i class="fa fa-instagram o_editable_media rounded shadow-sm"/>
|
|
</a>
|
|
<a
|
|
href="/website/social/youtube"
|
|
class="s_social_media_youtube"
|
|
target="_blank"
|
|
aria-label="YouTube"
|
|
>
|
|
<i class="fa fa-youtube-play o_editable_media rounded shadow-sm"/>
|
|
</a>
|
|
<a
|
|
href="/website/social/tiktok"
|
|
class="s_social_media_tiktok"
|
|
target="_blank"
|
|
aria-label="TikTok"
|
|
>
|
|
<i class="fa fa-tiktok o_editable_media rounded shadow-sm"/>
|
|
</a>
|
|
<a
|
|
href="/website/social/twitter"
|
|
class="s_social_media_twitter"
|
|
aria-label="Twitter"
|
|
>
|
|
<i class="fa fa-twitter o_editable_media rounded shadow-sm"/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<xpath expr="//span[hasclass('o_footer_copyright_name')]" position="replace">
|
|
<span class="small">
|
|
&copy;
|
|
<span class="o_footer_copyright_name align-baseline">Company name</span>
|
|
- <a href="#">Terms & Conditions</a>
|
|
- <a href="#">Privacy Policy</a>
|
|
</span>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="product_category_extra_link" name="Product Category Extra Link">
|
|
<button class="btn btn-link btn-sm pe-0" disabled="disabled">
|
|
<t t-if="len(categories) == 1">Category:</t>
|
|
<t t-else="">Categories:</t>
|
|
</button>
|
|
<t t-foreach="categories" t-as="category">
|
|
<button
|
|
class="btn btn-link btn-sm p-0 text-wrap"
|
|
t-out="category.name"
|
|
t-attf-onclick="location.href='#{shop_path}/category/#{slug(category)}';return false;"
|
|
/>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="template_header_mobile" inherit_id="website.template_header_mobile">
|
|
<xpath expr="//ul[hasclass('o_header_mobile_buttons_wrap')]" position="inside">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background_hover btn position-relative rounded-circle border-0 p-1 text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_default" inherit_id="website.template_header_default">
|
|
<xpath expr="//t[@t-call='website.placeholder_header_search_box']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background btn position-relative rounded-circle p-1 text-center text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_hamburger" inherit_id="website.template_header_hamburger">
|
|
<xpath expr="//t[@t-call='portal.placeholder_user_sign_in']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background btn position-relative rounded-pill p-1 text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_stretch" inherit_id="website.template_header_stretch">
|
|
<xpath expr="//t[@t-call='website.placeholder_header_social_links']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'border-start o_border_contrast'"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background_hover btn position-relative d-flex align-items-center h-100 rounded-0 p-2 text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'rounded'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_vertical" inherit_id="website.template_header_vertical">
|
|
<xpath expr="//t[@t-call='portal.placeholder_user_sign_in']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background btn position-relative rounded-circle p-1 text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_search" inherit_id="website.template_header_search">
|
|
<xpath expr="//t[@t-call='portal.placeholder_user_sign_in']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_text" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'border-start o_border_contrast'"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background_hover btn btn-sm d-flex align-items-center gap-1 h-100 rounded-0 p-2 text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'rounded'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_sales_one" inherit_id="website.template_header_sales_one">
|
|
<xpath expr="//t[@t-call='portal.user_dropdown']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'btn position-relative rounded-circle p-1 text-reset o_navlink_background'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_sales_two" inherit_id="website.template_header_sales_two">
|
|
<xpath expr="//t[@t-call='portal.placeholder_user_sign_in']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_text" t-value="True"/>
|
|
<t t-set="_icon_wrap_class" t-value="'o_navlink_background position-relative me-2 rounded-circle p-2 transition-base'"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_trigger_hover btn d-flex align-items-center fw-bold text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
<t t-set="_text_class" t-value="'small'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_sales_three" inherit_id="website.template_header_sales_three">
|
|
<xpath expr="//t[@t-call='website.placeholder_header_language_selector']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_text" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'position-relative'"/>
|
|
<t t-set="_link_class" t-value="'nav-link d-flex flex-row-reverse align-items-center text-uppercase fw-bold'"/>
|
|
<t t-set="_icon_wrap_class" t-value="'d-contains'"/>
|
|
<t t-set="_badge_class" t-value="'top-0 d-block ms-2'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_sales_four" inherit_id="website.template_header_sales_four">
|
|
<xpath expr="//t[@t-call='website.placeholder_header_call_to_action']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background btn position-relative rounded-pill p-1 text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_sidebar" inherit_id="website.template_header_sidebar">
|
|
<xpath expr="//t[@t-call='website.placeholder_header_brand']" position="after">
|
|
<ul class="list-unstyled d-flex gap-1 ms-auto mb-0">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background btn position-relative p-1 rounded-circle text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 rounded-pill mt-n1 me-n1'"/>
|
|
</t>
|
|
</ul>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_boxed" inherit_id="website.template_header_boxed">
|
|
<xpath expr="//t[@t-call='website.placeholder_header_search_box']" position="before">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_link_class" t-value="'o_navlink_background btn position-relative rounded-circle p-1 text-center text-reset'"/>
|
|
<t t-set="_badge_class" t-value="'position-absolute top-0 end-0 mt-n1 me-n1 rounded-pill'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Search Bar input-group template -->
|
|
<template id="search" name="Search Box" active="True">
|
|
<t t-call="website.website_search_box_input">
|
|
<t t-set="_form_classes" t-valuef="o_wsale_products_searchbar_form me-auto flex-grow-1 {{_form_classes}}"/>
|
|
<t t-set="_submit_classes" t-valuef="btn btn-{{navClass}}"/>
|
|
<t t-set="_input_classes" t-valuef="border-0 text-bg-{{navClass}}"/>
|
|
<t t-set="search_type" t-valuef="products"/>
|
|
<t t-set="action" t-value="keep(search=0)"/>
|
|
<t t-set="display_image" t-valuef="true"/>
|
|
<t t-set="display_description" t-valuef="true"/>
|
|
<t t-set="display_extra_link" t-valuef="true"/>
|
|
<t t-set="display_detail" t-valuef="true"/>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="product_variant_preview">
|
|
<!-- If gap-1 is changed make sure to update the margin in product_variant_preview.js -->
|
|
<div
|
|
t-attf-class="o_wsale_attribute_previewer d-flex align-items-center gap-1 flex-nowrap overflow-hidden {{
|
|
'show_on_hover' if product_ptavs[0]['ptav'].attribute_id.preview_variants == 'hover' and layout_mode != 'list' else ''
|
|
}}"
|
|
t-att-data-hidden-ptav-count="product_ptavs_data.get('hidden_ptavs_count')"
|
|
>
|
|
<t t-foreach="product_ptavs" t-as="previewed_ptav">
|
|
<t t-set="ptav" t-value="previewed_ptav['ptav']"/>
|
|
<t t-set="selected_attribute_value" t-value="ptav.product_attribute_value_id.id"/>
|
|
<a
|
|
class="o_product_variant_preview d-none z-2"
|
|
t-att-data-variant-image="previewed_ptav['variant_image_url']"
|
|
t-att-href="previewed_ptav['variant_url']"
|
|
>
|
|
<div
|
|
t-if="ptav.attribute_id.is_thumbnail_visible"
|
|
class="d-flex"
|
|
>
|
|
<label
|
|
class="css_attribute_preview_thumbnail cursor-pointer d-block"
|
|
t-att-style="'background:url(%s); background-size:cover; background-position: center center;' % previewed_ptav['variant_image_url']"
|
|
t-att-title="ptav.name"
|
|
/>
|
|
</div>
|
|
<div
|
|
t-elif="ptav.display_type == 'color'"
|
|
class="d-flex"
|
|
>
|
|
<t
|
|
t-if="ptav.product_attribute_value_id.image"
|
|
t-set="background_style"
|
|
t-value="'background:url(/web/image/product.attribute.value/%s/image); background-size:cover;' % ptav.product_attribute_value_id.id"
|
|
/>
|
|
<t
|
|
t-elif="not ptav.is_custom"
|
|
t-set="background_style"
|
|
t-value="'background: ' + str(ptav.html_color or ptav.name)"
|
|
/>
|
|
<label
|
|
t-att-style="background_style"
|
|
class="css_attribute_color cursor-pointer d-block"
|
|
t-att-title="ptav.name"
|
|
/>
|
|
</div>
|
|
<div
|
|
t-elif="ptav.display_type == 'image'"
|
|
class="d-flex"
|
|
>
|
|
<div
|
|
t-attf-class="css_attribute_image size_small cursor-pointer d-block rounded bg-body #{'custom_value' if ptav.is_custom else ''} #{'transparent' if (not ptav.is_custom and not ptav.html_color) else ''}"
|
|
t-att-title="ptav.name"
|
|
>
|
|
<div
|
|
class="o_bg_img_center w-100 h-100 rounded-1"
|
|
t-att-style="'background-image:url(/web/image/product.attribute.value/%s/image);' % ptav.product_attribute_value_id.id"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<span
|
|
t-else=""
|
|
t-attf-class="{{'' if ppr == 2 and layout_mode != 'list' else 'btn-sm'}} o_wsale_product_ptav_pill btn w-100 bg-body text-body"
|
|
>
|
|
<span class="o_wsale_product_ptav_pill_label d-block text-truncate" t-out="ptav.name"/>
|
|
</span>
|
|
</a>
|
|
</t>
|
|
<span name="hidden_ptavs_count" class="d-none ms-1 small z-2">
|
|
<a t-att-href="product_href"/>
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="pricelist_list" name="Pricelists Dropdown">
|
|
<t t-set="website_sale_pricelists" t-value="website_sale_pricelists or website.get_pricelist_available(show_visible=True)" />
|
|
<t t-set="hasPricelistDropdown" t-value="hasPricelistDropdown or (website_sale_pricelists and len(website_sale_pricelists)>1)"/>
|
|
<t t-set="pricelist" t-value="request.pricelist"/>
|
|
<t t-set="pricelist_label">Pricelist</t>
|
|
<div t-attf-class="o_pricelist_dropdown dropdown #{_classes if hasPricelistDropdown else 'd-none'}">
|
|
<a
|
|
role="button"
|
|
href="#"
|
|
t-attf-class="dropdown-toggle btn px-2"
|
|
data-bs-toggle="dropdown"
|
|
>
|
|
<small class="d-none d-md-inline opacity-75"><t t-out="pricelist_label"/>:</small>
|
|
<span class="d-none d-md-inline" t-out="pricelist and pricelist.name or ' - '" />
|
|
<span class="d-md-none"><t t-out="pricelist_label"/></span>
|
|
</a>
|
|
<div class="dropdown-menu" role="menu">
|
|
<t t-foreach="website_sale_pricelists" t-as="pl">
|
|
<t t-set="_is_active" t-value="pricelist.id == pl.id"/>
|
|
<a
|
|
role="menuitem"
|
|
t-att-href="'/shop/change_pricelist/%s' % pl.id"
|
|
t-attf-class="dropdown-item {{_is_active and 'active'}}"
|
|
t-att-aria-current="_is_active and 'true' or 'false'"
|
|
>
|
|
<span
|
|
class="switcher_pricelist"
|
|
t-att-data-pl_id="pl.id"
|
|
t-out="pl.name"
|
|
/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="products_breadcrumb" name="Products Breadcrumb">
|
|
<ol t-if="category" t-attf-class="breadcrumb #{_classes}">
|
|
<li class="breadcrumb-item">
|
|
<a
|
|
t-att-class="'d-none d-lg-inline' if len(category.parents_and_self) > 1 else ''"
|
|
t-att-href="keep(shop_path)"
|
|
>
|
|
Products
|
|
</a>
|
|
<span t-if="len(category.parents_and_self) > 1" class="d-lg-none">...</span>
|
|
</li>
|
|
<t t-foreach="category.parents_and_self" t-as="cat">
|
|
<li t-if="cat == category" class="breadcrumb-item">
|
|
<span class="d-inline-block" t-field="cat.name"/>
|
|
</li>
|
|
<li
|
|
t-else=""
|
|
t-attf-class="breadcrumb-item {{cat_index < cat_size - 2 and 'd-none d-lg-inline'}}"
|
|
>
|
|
<a
|
|
t-att-href="keep('%s/category/%s' % (shop_path, slug(cat)))"
|
|
t-field="cat.name"
|
|
/>
|
|
</li>
|
|
</t>
|
|
</ol>
|
|
</template>
|
|
|
|
<!-- /shop product listing -->
|
|
<template id="products" name="Products">
|
|
<t t-call="website.layout">
|
|
<t t-set="additional_title" t-value="category.name"/>
|
|
<t t-set="grid_block_name">Grid</t>
|
|
<t t-set="product_block_name">Product</t>
|
|
|
|
<!-- Qweb variable defining the class suffix for navbar items.
|
|
Change accordingly to the derired visual result (eg. `primary`, `dark`...)-->
|
|
<t t-set="navClass" t-valuef="light"/>
|
|
|
|
<!-- Check for active options: the stored value may be used in sub-templates too -->
|
|
<t t-set="opt_wsale_fullwidth" t-value="website.shop_page_container == 'fluid'"/>
|
|
<t t-set="opt_wsale_categories" t-value="is_view_active('website_sale.products_categories')"/>
|
|
<t t-set="opt_wsale_attributes" t-value="is_view_active('website_sale.products_attributes')"/>
|
|
<t t-set="opt_wsale_filter_price" t-value="is_view_active('website_sale.filter_products_price')"/>
|
|
<t t-set="opt_wsale_filter_tags" t-value="is_view_active('website_sale.filter_products_tags')"/>
|
|
<t t-set="opt_wsale_design_grid" t-value="'o_wsale_products_opt_design_grid' in website.shop_opt_products_design_classes"/>
|
|
|
|
<t t-set="opt_wsale_categories_top" t-value="is_view_active('website_sale.products_categories_top')"/>
|
|
<t t-set="opt_wsale_attributes_top" t-value="is_view_active('website_sale.products_attributes_top')"/>
|
|
<t t-set="opt_wsale_floating_bar" t-value="is_view_active('website_sale.floating_bar')"/>
|
|
|
|
<t t-set="opt_wsale_show_shop_title" t-value="is_view_active('website_sale.products_shop_title')"/>
|
|
<t t-set="opt_wsale_shop_title_align" t-value="is_view_active('website_sale.products_shop_title_align')"/>
|
|
|
|
<t t-set="opt_wsale_show_category_title" t-value="bool(category) and category.show_category_title"/>
|
|
<t t-set="opt_wsale_show_category_description" t-value="bool(category) and category.show_category_description"/>
|
|
<t t-set="opt_wsale_category_align" t-value="bool(category) and category.align_category_content"/>
|
|
|
|
<t t-set="website_sale_pricelists" t-value="website.get_pricelist_available(show_visible=True)" />
|
|
<t t-set="website_sale_sortable" t-value="website._get_product_sort_mapping()"/>
|
|
|
|
<t t-set="hasLeftColumn" t-value="opt_wsale_categories or opt_wsale_attributes"/>
|
|
|
|
<t t-set="isFilteringByPrice" t-if="opt_wsale_filter_price" t-value="float_round(available_min_price, 2) != float_round(min_price, 2) or float_round(available_max_price, 2) != float_round(max_price, 2)"/>
|
|
<t t-set="hasPricelistDropdown" t-value="website_sale_pricelists and len(website_sale_pricelists)>1"/>
|
|
<t t-set="isSortingBy" t-value="[sort for sort in website_sale_sortable if sort[0]==request.params.get('order', '')]"/>
|
|
|
|
<t
|
|
t-set="wsale_has_filters_btn"
|
|
t-value="opt_wsale_categories or opt_wsale_attributes or opt_wsale_attributes_top"
|
|
/>
|
|
<t
|
|
t-set="wsale_has_filters_btn_lg"
|
|
t-value="opt_wsale_attributes_top"
|
|
/>
|
|
|
|
<div id="wrap" class="js_sale o_wsale_products_page">
|
|
<div class="oe_structure oe_empty oe_structure_not_nearest" id="oe_structure_website_sale_products_1"/>
|
|
<div
|
|
id="o_wsale_container"
|
|
t-attf-class="oe_website_sale
|
|
{{opt_wsale_fullwidth and 'container-fluid o_wsale_page_fluid' or 'o_wsale_page_contained container'}}
|
|
{{opt_wsale_categories_top and 'o_wsale_has_filmstrip'}}
|
|
{{opt_wsale_floating_bar and 'o_wsale_has_floating_bar'}}
|
|
{{hasLeftColumn and 'o_wsale_has_sidebar'}}
|
|
{{editionPreviews and 'o_wsale_edit_preview_enabled'}}
|
|
{{opt_wsale_design_grid and 'o_wsale_uses_grid_design'}}"
|
|
t-att-data-ppg="ppg"
|
|
t-att-data-ppr="ppr"
|
|
t-att-data-default-sort="website.shop_default_sort"
|
|
>
|
|
<div class="row o_wsale_products_main_row flex-nowrap">
|
|
<aside
|
|
t-if="hasLeftColumn"
|
|
id="products_grid_before"
|
|
class="d-none d-lg-block position-sticky align-self-start col clearfix pt-2"
|
|
>
|
|
<t t-call="website_sale.sidebar_dropzone_at_top"/>
|
|
<div class="o_wsale_products_grid_before_rail vh-100 ms-n2 px-lg-2 ps-2 overflow-y-scroll">
|
|
<t
|
|
t-set="is_sidebar_collapsible"
|
|
t-value="is_view_active('website_sale.products_categories_list_collapsible')"
|
|
/>
|
|
<div
|
|
t-if="opt_wsale_categories"
|
|
t-att-class="'products_categories'
|
|
+ (' accordion accordion-flush' if is_sidebar_collapsible else ' mb-3')"
|
|
>
|
|
<t t-call="website_sale.products_categories_list"/>
|
|
</div>
|
|
<t
|
|
t-set="show_price_filter"
|
|
t-value="opt_wsale_filter_price and opt_wsale_attributes"
|
|
/>
|
|
<div
|
|
t-if="attrib_values or tags or (
|
|
show_price_filter and isFilteringByPrice
|
|
)"
|
|
class="py-3 border-bottom"
|
|
>
|
|
<a
|
|
t-att-href="keep(attribute_values=0, tags=0, min_price=0, max_price=0)"
|
|
t-attf-class="btn btn-{{navClass}} d-flex align-items-center py-1"
|
|
title="Clear Filters"
|
|
>
|
|
<small class="mx-auto"><b>Clear Filters</b></small>
|
|
<i class="oi oi-close" role="presentation"/>
|
|
</a>
|
|
</div>
|
|
<div
|
|
t-attf-class="products_attributes_filters d-empty-none {{opt_wsale_categories and 'border-top'}} {{'pt-3' if not is_sidebar_collapsible else ''}}"
|
|
/>
|
|
<t
|
|
t-if="show_price_filter"
|
|
t-call="website_sale.filter_products_price"
|
|
>
|
|
<t
|
|
t-set="_classes"
|
|
t-valuef="{{is_sidebar_collapsible and 'accordion accordion-flush'}} {{is_sidebar_collapsible and (opt_wsale_categories or len(attributes) > 0) and 'border-top'}}"
|
|
/>
|
|
</t>
|
|
<t t-call="website_sale.sidebar_dropzone_at_bottom"/>
|
|
</div>
|
|
</aside>
|
|
<div id="products_grid" class="col">
|
|
<t
|
|
t-set="_header_has_content"
|
|
t-value="category or opt_wsale_categories_top or (not opt_wsale_floating_bar and (opt_wsale_attributes_top or is_view_active('website_sale.search') or is_view_active('website_sale.sort') or hasPricelistDropdown))"
|
|
/>
|
|
|
|
<header
|
|
id="o_wsale_products_header"
|
|
t-att-data-category-id="category.id"
|
|
t-att-data-category-name="category.name"
|
|
t-attf-class="d-flex flex-column gap-2
|
|
{{_header_has_content and 'pt-4 pb-3' or opt_wsale_design_grid and 'py-3'}}
|
|
{{category and 'o_wsale_products_header_is_category' or 'o_wsale_products_header_is_shop'}}
|
|
{{opt_wsale_show_category_title and 'o_wsale_products_header_show_category_title'}}
|
|
{{opt_wsale_show_category_description and 'o_wsale_products_header_show_category_description'}}
|
|
{{opt_wsale_category_align and 'o_wsale_products_header_category_center_content'}}
|
|
{{opt_wsale_show_shop_title and 'o_wsale_products_header_show_shop_title'}}
|
|
{{opt_wsale_shop_title_align and 'o_wsale_products_header_shop_center_content'}}"
|
|
>
|
|
<t t-if="category and not search" t-call="website_sale.products_breadcrumb">
|
|
<t
|
|
t-set="_classes"
|
|
t-valuef="w-100 p-0 mb-0 small {{not opt_wsale_show_category_title and 'mb-2 mb-lg-0'}}"
|
|
/>
|
|
</t>
|
|
<h1
|
|
t-if="category or search"
|
|
t-attf-class="o_wsale_category_title d-flex gap-1 align-items-center mb-0 lh-lg {{search and bool(category) and 'mb-2'}} {{search and 'o_wsale_category_title_is_search base-fs font-sans-serif' or 'fs-3'}}"
|
|
>
|
|
<t t-if="search and category">
|
|
<span class="text-muted">Searching</span>
|
|
<span class="badge text-bg-info fw-normal base-fs">
|
|
<t t-out="search"/>
|
|
<a
|
|
title="Clear search for this category"
|
|
t-att-href="keep(search=0)"
|
|
class="link-info"
|
|
>
|
|
<i class="oi oi-close" role="img"/>
|
|
</a>
|
|
</span>
|
|
<span class="text-muted">in</span>
|
|
<span class="badge text-bg-primary fw-normal base-fs">
|
|
<t t-out="category.name"/>
|
|
<a
|
|
title="Search globally"
|
|
t-att-href="keep(shop_path, search=search)"
|
|
>
|
|
<i class="oi oi-close" role="img"/>
|
|
</a>
|
|
</span>
|
|
</t>
|
|
<span t-elif="search">
|
|
<span class="text-muted">Search results for </span>'<strong t-out="search"/>' <small t-if="search_count" class="text-muted"> (<t t-out="search_count"/>)</small>
|
|
</span>
|
|
<t t-elif="category">
|
|
<span t-field="category.name"/>
|
|
</t>
|
|
</h1>
|
|
<h1
|
|
class="o_wsale_shop_title h4-fs"
|
|
t-if="not category and not search"
|
|
>
|
|
All products
|
|
</h1>
|
|
<t t-if="category">
|
|
<t t-set='editor_msg'>
|
|
Drag building blocks here to customize the header for
|
|
"<t t-out='category.name'/>" category.
|
|
</t>
|
|
<div
|
|
id="category_header"
|
|
class="o_wsale_category_description"
|
|
t-att-data-editor-message="editor_msg"
|
|
t-field="category.website_description"
|
|
/>
|
|
</t>
|
|
<t t-else="">
|
|
<t t-set="editor_msg">
|
|
Drag building blocks here to customize the header for the shop page.
|
|
</t>
|
|
<div
|
|
id="oe_structure_products_header_shop"
|
|
class="oe_structure oe_empty"
|
|
t-att-data-editor-message="editor_msg"
|
|
/>
|
|
</t>
|
|
<t t-if="opt_wsale_categories_top and category_entries"
|
|
t-call="website_sale.filmstrip_categories"
|
|
entries="category_entries"
|
|
/>
|
|
<div
|
|
t-if="not opt_wsale_floating_bar"
|
|
class="products_header btn-toolbar flex-nowrap align-items-center justify-content-between gap-1 gap-lg-2 gap-xl-3"
|
|
>
|
|
<t t-if="is_view_active('website_sale.search')">
|
|
<t t-set="_has_all_sibligns" t-value="hasPricelistDropdown and is_view_active('website_sale.sort') and wsale_has_filters_btn"></t>
|
|
<!-- Desktop Search button -->
|
|
<div t-attf-class="o_wsale_products_header_search_form_container {{ _has_all_sibligns and 'd-none d-sm-inline-block'}} col-xl-5 me-auto {{not hasLeftColumn and 'col-xl-6'}}">
|
|
<t t-set="search_term">Search in</t>
|
|
<t t-if="category" t-set="placeholder" t-valuef="{{ search_term }} {{ category.name }}"/>
|
|
<t t-call="website_sale.search" placeholder="placeholder" search="original_search or search"/>
|
|
</div>
|
|
<!-- Mobile Search button -->
|
|
<a
|
|
t-attf-class="btn o_not_editable {{_has_all_sibligns and 'd-inline-block d-sm-none' or 'd-none'}} me-auto btn-{{navClass}}"
|
|
data-bs-target="#o_wsale_search_modal"
|
|
data-bs-toggle="modal"
|
|
role="button"
|
|
title="Search Products"
|
|
href="#"
|
|
>
|
|
<i class="oi oi-search" role="img"/>
|
|
</a>
|
|
</t>
|
|
<t
|
|
t-if="hasPricelistDropdown"
|
|
t-call="website_sale.pricelist_list"
|
|
/>
|
|
<t
|
|
t-if="is_view_active('website_sale.sort')"
|
|
t-call="website_sale.sort"
|
|
/>
|
|
<button
|
|
t-if="wsale_has_filters_btn"
|
|
t-attf-class="btn btn-{{navClass}} position-relative {{not wsale_has_filters_btn_lg and 'd-lg-none'}} {{not is_view_active('website_sale.search') and 'ms-auto'}} text-nowrap"
|
|
title="Filters"
|
|
data-bs-toggle="offcanvas"
|
|
data-bs-target="#o_wsale_offcanvas"
|
|
>
|
|
<i class="oi oi-settings-adjust" role="img"/>
|
|
<small t-attf-class="ms-1 {{(hasPricelistDropdown and is_view_active('website_sale.search') and is_view_active('website_sale.sort')) and 'd-none d-md-inline-block'}}">Filters</small>
|
|
<span
|
|
t-if="isFilteringByPrice or attrib_set or tags"
|
|
t-attf-class="position-absolute top-0 start-100 translate-middle border border-{{navClass}} rounded-circle bg-danger p-1"
|
|
>
|
|
<span class="visually-hidden">filters active</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div t-if="original_search and products" class="alert alert-warning mt8">
|
|
No results found for '<span t-out="original_search"/>'. Showing results for '<span t-out="search"/>'.
|
|
</div>
|
|
</header>
|
|
|
|
<div t-if="products" class="o_wsale_products_grid_table_wrapper">
|
|
<t t-set="grid_md_allow_custom_cols" t-value="hasLeftColumn"/>
|
|
<t
|
|
t-set="grid_md_use_3col"
|
|
t-value="(not hasLeftColumn and ppr == 4) or (opt_wsale_fullwidth and ppr > 3)"
|
|
/>
|
|
|
|
<section
|
|
id="o_wsale_products_grid"
|
|
t-attf-class="o_wsale_products_grid_table grid
|
|
{{grid_md_allow_custom_cols and 'o_wsale_products_grid_table_md'}}
|
|
{{website.shop_opt_products_design_classes}}"
|
|
t-attf-style="--o-wsale-ppr: {{ppr}}; --o-wsale-ppg: {{ppg}}; --o-wsale-products-grid-gap: {{gap}};"
|
|
t-att-data-name="grid_block_name"
|
|
>
|
|
<t t-foreach="bins" t-as="tr_product">
|
|
<t t-foreach="tr_product" t-as="td_product">
|
|
<t t-if="td_product">
|
|
<t t-set="col_height" t-value="td_product['y']"/>
|
|
<t t-set="col_width"
|
|
t-value="12 // ppr * td_product['x']"/>
|
|
<t t-set="col_class" t-valuef="g-col-6"/>
|
|
<t t-set="col_class_lg"
|
|
t-value="'g-col-lg-' + str(col_width)"/>
|
|
<t t-set="col_class_md"
|
|
t-value="grid_md_allow_custom_cols and ('g-col-md-' + str(col_width)) or grid_md_use_3col and 'g-col-md-4' or 'g-col-md-6'"/>
|
|
<t t-set="col_is_stretched"
|
|
t-value="(td_product['x'] >= td_product['y'] * 2)"/>
|
|
<t t-set="col_is_custom_portrait"
|
|
t-value="not col_is_stretched and (col_height > td_product['x'])"/>
|
|
<t t-set="product" t-value="td_product['product']"/>
|
|
<t t-set="template_price_vals" t-value="get_product_prices(product)"/>
|
|
<t
|
|
t-set="ribbon"
|
|
t-value="product._get_ribbon(
|
|
price_vals=template_price_vals,
|
|
auto_assign_ribbons=auto_assign_ribbons,
|
|
variant=product_variants[product],
|
|
)"
|
|
/>
|
|
<div
|
|
t-attf-class="oe_product {{col_is_custom_portrait and 'oe_product_custom_portrait'}} {{col_class}} {{col_class_md}} {{col_class_lg}} {{col_is_stretched and 'oe_product_size_stretch'}}"
|
|
t-attf-style="--o-wsale-products-grid-product-col-height: {{col_height}};"
|
|
t-att-data-colspan="td_product['x'] != 1 and td_product['x']"
|
|
t-att-data-rowspan="td_product['y'] != 1 and td_product['y']"
|
|
t-att-data-name="product_block_name"
|
|
>
|
|
<div t-attf-class="o_wsale_product_grid_wrapper position-relative h-100 o_wsale_product_grid_wrapper_#{td_product['x']}_#{td_product['y']}">
|
|
<t t-call="website_sale.products_item">
|
|
<t
|
|
t-set="design_field"
|
|
t-value="website.shop_opt_products_design_classes"
|
|
/>
|
|
<t
|
|
t-set="product_href"
|
|
t-value="product._get_product_url(category, product_query_params, grouped_attributes_values)"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</section>
|
|
</div>
|
|
<div t-else="" class="text-center text-muted mt128 mb256">
|
|
<t t-if="not search">
|
|
<div class="mt-5 mb-3"><t t-call="website.empty_search_svg"/></div>
|
|
<h3
|
|
class="mt8"
|
|
style="text-align: center;"
|
|
>
|
|
No product defined
|
|
</h3>
|
|
<p
|
|
t-if="category"
|
|
style="text-align: center;"
|
|
>
|
|
No product defined in this category.
|
|
</p>
|
|
</t>
|
|
<t t-else="">
|
|
<div class="mt-5 mb-3"><t t-call="website.empty_search_svg"/></div>
|
|
<h3 class="mt8" style="text-align: center;">No results</h3>
|
|
<p style="text-align: center;">
|
|
No results for "<strong t-out='search'/>"<t t-if="category"> in category "<strong t-out="category.display_name"/>"</t>.
|
|
</p>
|
|
<p style="text-align: center;">
|
|
<a
|
|
t-if="category"
|
|
t-att-href="keep(shop_path, search=search)"
|
|
t-attf-title="Search {{search}} globally"
|
|
class="d-block pb-5"
|
|
>
|
|
Search globally
|
|
</a>
|
|
</p>
|
|
</t>
|
|
<div
|
|
t-ignore="true"
|
|
groups="sales_team.group_sale_manager"
|
|
class="alert alert-info d-inline-block mx-auto text-center"
|
|
>
|
|
Click <i>'New'</i> in the top-right corner to create your first product.
|
|
</div>
|
|
</div>
|
|
<div id="o_wsale_pager" t-attf-class="products_pager d-flex justify-content-center {{opt_wsale_design_grid and 'py-4' or 'py-5 pb-4'}}">
|
|
<t t-call="website.pager"/>
|
|
</div>
|
|
<t t-if="category">
|
|
<t t-set='footer_editor_message'>
|
|
Drag building blocks here to customize the footer for
|
|
"<t t-out='category.name'/>" category.
|
|
</t>
|
|
<div
|
|
class="mb16"
|
|
id="category_footer"
|
|
t-att-data-editor-message="footer_editor_message"
|
|
t-field="category.website_footer"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
|
|
<t t-call="website_sale.o_wsale_offcanvas"/>
|
|
</div>
|
|
<div class="oe_structure oe_empty oe_structure_not_nearest" id="oe_structure_website_sale_products_2"/>
|
|
</div>
|
|
<div
|
|
t-if="is_view_active('website_sale.search')"
|
|
id="o_wsale_search_modal"
|
|
class="modal fade css_editable_mode_hidden"
|
|
aria-hidden="true"
|
|
tabindex="-1"
|
|
>
|
|
<div class="modal-dialog modal-lg pt-5">
|
|
<div class="modal-content mt-5 bg-transparent border-0">
|
|
<div class="o_container_small">
|
|
<t t-call="website.website_search_box_input">
|
|
<t t-set="default_style" t-valuef="true"/>
|
|
<t t-set="search_type" t-valuef="products"/>
|
|
<t t-set="_classes" t-valuef="input-group rounded o_cc o_cc1 mb-1"/>
|
|
<t t-set="_input_classes" t-valuef="border-0 p-3"/>
|
|
<t t-set="_submit_classes" t-valuef="px-4"/>
|
|
<t t-set="search_term">Search in</t>
|
|
<t t-if="category" t-set="placeholder" t-valuef="{{ search_term }} {{ category.name }}"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="website_sale.sidebar_dropzone_at_top">
|
|
<div id="oe_structure_website_sale_sidebar_top" class="oe_structure">
|
|
<section
|
|
class="s_text_block oe_unmovable oe_unremovable"
|
|
data-snippet="s_text_block"
|
|
data-name="Text"
|
|
>
|
|
<p/>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.sidebar_dropzone_at_bottom">
|
|
<div id="oe_structure_website_sale_sidebar_bottom" class="oe_structure">
|
|
<section
|
|
class="s_text_block oe_unmovable oe_unremovable"
|
|
data-snippet="s_text_block"
|
|
data-name="Text"
|
|
>
|
|
<p/>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- (Option) Mobile: Show 1 product for row -->
|
|
<template
|
|
id="website_sale.products_mobile_cols_single"
|
|
active="False"
|
|
name="Mobile: Show 1 product for row"
|
|
inherit_id="website_sale.products"
|
|
>
|
|
<xpath expr="//t[@t-set='col_class']" position="attributes">
|
|
<attribute name="t-valuef">g-col-12</attribute>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="website_sale.sort" name="Sort-by Template">
|
|
<div t-attf-class="o_sortby_dropdown dropdown dropdown_sorty_by {{_classes}}">
|
|
<t
|
|
t-set="_current_sort"
|
|
t-value="isSortingBy and isSortingBy[0][0] or website.shop_default_sort"
|
|
/>
|
|
<t t-set="_sort_label">Sort By</t>
|
|
<a
|
|
role="button"
|
|
href="#"
|
|
t-attf-class="dropdown-toggle btn px-2"
|
|
data-bs-toggle="dropdown"
|
|
>
|
|
<span class="d-md-none"><t t-out="_sort_label"/></span>
|
|
<small class="d-none d-md-inline opacity-75"><t t-out="_sort_label"/>:</small>
|
|
<span class="d-none d-md-inline">
|
|
<t t-if="isSortingBy" t-out="isSortingBy[0][1]"/>
|
|
<!-- Prevent editing the content in website builder -->
|
|
<span t-else="" t-out="dict(website_sale_sortable)[website.shop_default_sort]"/>
|
|
</span>
|
|
<i t-if="_icon_classes" t-attf-class="fa fa-sort {{_icon_classes}}" role="img"/>
|
|
</a>
|
|
<div class="dropdown-menu dropdown-menu-end" role="menu">
|
|
<t t-foreach="website_sale_sortable" t-as="sortby">
|
|
<t t-set="_is_active" t-value="_current_sort == sortby[0]"/>
|
|
<a
|
|
role="menuitem"
|
|
rel="noindex,nofollow"
|
|
t-att-href="keep(order=sortby[0])"
|
|
t-attf-class="dropdown-item {{_is_active and 'active'}}"
|
|
>
|
|
<span t-out="sortby[1]"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.products_categories" active="False" name="Categories in Left Side "/>
|
|
<template id="website_sale.products_categories_top" active="True" name="Categories in top-nav"/>
|
|
<template id="website_sale.products_attributes_top" active="False" name="Attributes in top-nav"/>
|
|
|
|
<template id="website_sale.products_shop_title" active="True" name="Show Shop Title"/>
|
|
<template id="website_sale.products_shop_title_align" active="False" name="Align Shop Title"/>
|
|
|
|
<template id="website_sale.filter_color_attributes" name="Color attributes in Filter">
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<t t-set="img_style"
|
|
t-value="'background:url(/web/image/product.attribute.value/%s/image); background-size:cover;' % v.id if v.image else ''"
|
|
/>
|
|
<t t-set="color_style"
|
|
t-value="'background: ' + str(v.html_color or v.name if not v.is_custom else '')"
|
|
/>
|
|
<label t-attf-style="#{img_style or color_style}"
|
|
t-attf-class="css_attribute_color cursor-pointer #{'active' if v.id in attrib_set else ''}"
|
|
>
|
|
<input type="checkbox"
|
|
name="attribute_value"
|
|
class="cursor-pointer"
|
|
t-att-value="'%s-%s' % (a.id, v.id)"
|
|
t-att-checked="'checked' if v.id in attrib_set else None"
|
|
t-att-title="v.name"
|
|
/>
|
|
</label>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="website_sale.filter_image_attributes" name="Image attributes in Filter">
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<label
|
|
t-attf-class="css_attribute_image size_small position-relative rounded-3 bg-body text-center cursor-pointer #{'active' if v.id in attrib_set else ''} #{'custom_value' if v.is_custom else ''} #{'transparent' if (not v.is_custom and not v.html_color) else ''}"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
name="attribute_value"
|
|
t-att-value="'%s-%s' % (a.id, v.id)"
|
|
t-att-checked="'checked' if v.id in attrib_set else None"
|
|
t-att-title="v.name"
|
|
class="w-100 h-100 opacity-0 cursor-pointer"
|
|
/>
|
|
<div
|
|
class="oe_img_bg o_bg_img_center position-absolute top-0 w-100 h-100 rounded-1"
|
|
t-att-style="'background-image:url(/web/image/product.attribute.value/%s/image); background-size:cover;' % v.id if v.image else ''"
|
|
/>
|
|
</label>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="website_sale.filter_select_attributes" name="Select attributes in Filter">
|
|
<select class="form-select css_attribute_select" name="attribute_value">
|
|
<option value="" selected="true">All <t t-out="a.name"/></option>
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<option
|
|
t-att-value="'%s-%s' % (a.id,v.id)"
|
|
t-out="v.name"
|
|
t-att-selected="v.id in attrib_set"
|
|
/>
|
|
</t>
|
|
</select>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.filter_radio_and_multi_attributes"
|
|
name="Radio and Multi attributes in Filter"
|
|
>
|
|
<t
|
|
t-set="sorted_values"
|
|
t-value="sorted(a.value_ids, key=lambda v: v.id not in attrib_set)"
|
|
/>
|
|
<t t-if="len(sorted_values) <= 20">
|
|
<div
|
|
t-foreach="sorted_values[:8]"
|
|
t-as="v"
|
|
t-attf-class="{{'list-group-item border-0 ps-0 pb-0' if isMobile else ''}}"
|
|
>
|
|
<div class="form-check mb-1">
|
|
<input
|
|
type="checkbox"
|
|
name="attribute_value"
|
|
class="form-check-input"
|
|
t-att-id="'%s-%s' % (a.id,v.id)"
|
|
t-att-value="'%s-%s' % (a.id,v.id)"
|
|
t-att-checked="v.id in attrib_set"
|
|
/>
|
|
<label
|
|
class="form-check-label fw-normal"
|
|
t-att-for="'%s-%s' % (a.id,v.id)"
|
|
t-field="v.name"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<t t-if="len(sorted_values) > 8">
|
|
<div
|
|
t-attf-id="o_wsale_remaining_values_{{a.id}}"
|
|
class="accordion-collapse collapse"
|
|
t-attf-aria-labelledby="o_wsale_view_more_{{a.id}}_header"
|
|
>
|
|
<div
|
|
t-foreach="sorted_values[8:]"
|
|
t-as="v"
|
|
t-attf-class="{{'list-group-item border-0 ps-0 pb-0' if isMobile else ''}}"
|
|
>
|
|
<div class="form-check mb-1">
|
|
<input
|
|
type="checkbox"
|
|
name="attribute_value"
|
|
class="form-check-input"
|
|
t-att-id="'%s-%s' % (a.id,v.id)"
|
|
t-att-value="'%s-%s' % (a.id,v.id)"
|
|
t-att-checked="v.id in attrib_set"
|
|
/>
|
|
<label
|
|
class="form-check-label fw-normal"
|
|
t-att-for="'%s-%s' % (a.id,v.id)"
|
|
t-field="v.name"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
t-attf-id="o_wsale_view_more_{{a.id}}_header"
|
|
t-attf-class="accordion-header {{'mb-0' if isMobile else ''}}"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="o_wsale_view_more_btn btn btn-link px-0 collapsed"
|
|
data-bs-toggle="collapse"
|
|
t-attf-data-bs-target="#o_wsale_remaining_values_{{a.id}}"
|
|
t-attf-aria-controls="o_wsale_remaining_values_{{a.id}}"
|
|
aria-expanded="false"
|
|
>
|
|
View More
|
|
</button>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<t t-elif="len(sorted_values) > 20">
|
|
<div t-attf-class="{{'my-3' if isMobile else 'mb-3'}}">
|
|
<input
|
|
type="text"
|
|
class="o_wsale_attribute_search_bar form-control"
|
|
t-attf-placeholder="Search {{a.name}} ({{len(a.value_ids)}})"
|
|
t-att-data-container-id="'searchable-values-%s' % a.id"
|
|
/>
|
|
</div>
|
|
<div
|
|
t-attf-id="searchable-values-{{ a.id }}"
|
|
class="overflow-auto"
|
|
t-attf-style="max-height:{{'290px' if isMobile else '220px'}};"
|
|
>
|
|
<t t-foreach="sorted_values" t-as="v">
|
|
<div
|
|
t-attf-class="form-check mb-1 {{'list-group-item border-0 ms-3 pb-0' if isMobile else 'ms-1'}}"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
name="attribute_value"
|
|
class="form-check-input"
|
|
t-att-id="'%s-%s' % (a.id,v.id)"
|
|
t-att-value="'%s-%s' % (a.id,v.id)"
|
|
t-att-checked="v.id in attrib_set"
|
|
/>
|
|
<label
|
|
class="form-check-label fw-normal"
|
|
t-att-for="'%s-%s' % (a.id,v.id)"
|
|
t-field="v.name"
|
|
/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="website_sale.filter_pills_attributes" name="Pills attributes in Filter">
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<input
|
|
type="checkbox"
|
|
name="attribute_value"
|
|
class="btn-check"
|
|
t-att-id="'%s-%s' % (a.id,v.id)"
|
|
t-att-value="'%s-%s' % (a.id,v.id)"
|
|
t-att-checked="v.id in attrib_set"
|
|
autocomplete="off"
|
|
/>
|
|
<label
|
|
t-attf-class="btn border {{'active bg-primary-subtle border-primary text-primary-emphasis' if v.id in attrib_set else ''}}"
|
|
t-att-for="'%s-%s' % (a.id,v.id)"
|
|
t-field="v.name"
|
|
/>
|
|
</t>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.product_attribute_filters_form"
|
|
name="Product Attribute Filters Form"
|
|
>
|
|
<form
|
|
t-attf-class="js_attributes {{('accordion accordion-flush d-flex flex-column ' + ('border-bottom' if attributes else '')) if isMobile else 'position-relative mb-2'}}"
|
|
method="get"
|
|
t-att-action="keep(attribute_values=0, tags=0)"
|
|
>
|
|
<t
|
|
t-set="visible_attributes"
|
|
t-value="len([a for a in attributes if a.value_ids and len(a.value_ids) > 1])"
|
|
/>
|
|
|
|
<t t-foreach="attributes" t-as="a">
|
|
<t t-set="_status" t-value="'inactive'"/>
|
|
<t t-foreach="a.value_ids" t-as="v" t-if="v.id in attrib_set" t-set="_status" t-value="'active'"/>
|
|
<t t-set="_expand_attributes" t-value="_status == 'active' or visible_attributes < 4"/>
|
|
<div
|
|
t-if="a.value_ids and len(a.value_ids) > 1"
|
|
t-attf-class="accordion-item {{('border-top-0 ' + ('order-1' if _status == 'active' else 'order-2')) if isMobile else 'nav-item mb-1 rounded-0'}}"
|
|
>
|
|
<h2
|
|
t-if="isMobile"
|
|
class="accordion-header mb-0"
|
|
t-attf-id="o_wsale_offcanvas_attribute_{{a.id}}_header"
|
|
>
|
|
<button
|
|
t-attf-class="o_wsale_offcanvas_title accordion-button rounded-0 {{'' if _expand_attributes else 'collapsed'}}"
|
|
type="button"
|
|
t-att-data-status="_status"
|
|
data-bs-toggle="collapse"
|
|
t-attf-data-bs-target="#o_wsale_offcanvas_attribute_{{a.id}}"
|
|
t-att-aria-expanded="_expand_attributes"
|
|
t-attf-aria-controls="o_wsale_offcanvas_attribute_{{a.id}}"
|
|
>
|
|
<b t-out="a.name"/>
|
|
</button>
|
|
</h2>
|
|
<h6 t-else="" class="mb-3">
|
|
<b class="d-none d-lg-block" t-field="a.name"/>
|
|
</h6>
|
|
<div
|
|
t-attf-id="{{'o_wsale_offcanvas_attribute_' + str(a.id) if isMobile else 'o_products_attributes_' + str(a.id)}}"
|
|
t-attf-class="{{'accordion-collapse collapse ' + ('show' if _expand_attributes else '') if isMobile else ''}}"
|
|
>
|
|
<div t-attf-class="{{'accordion-body pt-0' if isMobile else ''}}">
|
|
<div
|
|
t-if="a.display_type == 'select'"
|
|
t-attf-class="{{'my-2' if isMobile else 'mb-3'}}"
|
|
>
|
|
<t t-call="website_sale.filter_select_attributes"/>
|
|
</div>
|
|
<div
|
|
t-elif="a.display_type == 'color'"
|
|
t-attf-class="d-flex flex-wrap gap-2 {{'pt-1 pb-3' if isMobile else 'mb-3'}}"
|
|
>
|
|
<t t-call="website_sale.filter_color_attributes"/>
|
|
</div>
|
|
<div
|
|
t-elif="a.display_type == 'image'"
|
|
t-attf-class="d-flex flex-wrap gap-2 #{'pt-1 pb-3' if isMobile else 'mb-3'}"
|
|
>
|
|
<t t-call="website_sale.filter_image_attributes"/>
|
|
</div>
|
|
<div
|
|
t-elif="a.display_type in ('radio', 'multi')"
|
|
t-attf-class="{{'list-group list-group-flush' if isMobile else 'flex-column mb-3'}}"
|
|
>
|
|
<t t-call="website_sale.filter_radio_and_multi_attributes">
|
|
<t t-set="isMobile" t-value="isMobile"/>
|
|
</t>
|
|
</div>
|
|
<div
|
|
t-elif="a.display_type == 'pills'"
|
|
t-attf-class="d-flex flex-wrap gap-2 {{'my-2' if isMobile else 'mb-3'}}"
|
|
data-bs-toggle="buttons"
|
|
>
|
|
<t t-call="website_sale.filter_pills_attributes"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<t t-if="isMobile">
|
|
<t t-if="opt_wsale_filter_tags and (opt_wsale_attributes or opt_wsale_attributes_top)">
|
|
<t t-set="_status" t-value="'inactive'"/>
|
|
<t t-foreach="all_tags" t-as="v" t-if="v.id in tags" t-set="_status" t-value="'active'"/>
|
|
<div t-if="all_tags">
|
|
<h2 class="accordion-header mb-0" t-attf-id="o_wsale_offcanvas_tags_header">
|
|
<button
|
|
t-attf-class="o_wsale_offcanvas_title accordion-button border-top rounded-0 {{not tags and 'collapsed'}}"
|
|
type="button"
|
|
t-att-data-status="_status"
|
|
data-bs-toggle="collapse"
|
|
t-attf-data-bs-target="#o_wsale_offcanvas_tags"
|
|
t-att-aria-expanded="_status == 'active' and 'True' or 'False'"
|
|
t-attf-aria-controls="o_wsale_offcanvas_tags"
|
|
>
|
|
<b>Tags</b>
|
|
</button>
|
|
</h2>
|
|
<div
|
|
t-attf-id="o_wsale_offcanvas_tags"
|
|
t-attf-class="accordion-collapse collapse {{(_status == 'active') and 'show'}}"
|
|
t-att-aria-expanded="(_status == 'active') and 'True' or 'False'"
|
|
t-attf-aria-labelledby="o_wsale_offcanvas_tags_header"
|
|
>
|
|
<div class="accordion-body pt-0">
|
|
<div class="list-group list-group-flush">
|
|
<t t-call="website_sale.filter_products_tags_list">
|
|
<t t-set="all_tags" t-value="all_tags"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<t t-else="">
|
|
<t
|
|
t-if="opt_wsale_filter_tags and opt_wsale_attributes"
|
|
t-call="website_sale.filter_products_tags"
|
|
>
|
|
<t t-set="all_tags" t-value="all_tags"/>
|
|
</t>
|
|
</t>
|
|
</form>
|
|
</template>
|
|
|
|
<!-- OffCanvas Nav -->
|
|
<template id="website_sale.o_wsale_offcanvas" name="Offcanvas">
|
|
<aside id="o_wsale_offcanvas"
|
|
class="o_website_offcanvas offcanvas offcanvas-end p-0">
|
|
<div class="offcanvas-header justify-content-end">
|
|
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"/>
|
|
</div>
|
|
<div
|
|
t-if="is_view_active('website_sale.search') and (opt_wsale_attributes or opt_wsale_attributes_top)"
|
|
class="offcanvas-body flex-grow-0 overflow-visible"
|
|
>
|
|
<t t-call="website_sale.search">
|
|
<t t-set="search_term">Search in</t>
|
|
<t t-if="category" t-set="placeholder" t-valuef="{{ search_term }} {{ category.name }}"/>
|
|
<t t-set="search" t-value="original_search or search"/>
|
|
<t t-set="_s_searchbar_autocomplete_classes" t-valuef="bg-primary"> </t>
|
|
</t>
|
|
</div>
|
|
<div id="o_wsale_offcanvas_content" class="accordion accordion-flush flex-grow-1 overflow-auto">
|
|
<!-- Show sorting only if it's not avavailbe in the topbar already -->
|
|
<div t-if="not is_view_active('website_sale.sort')" class="accordion-item">
|
|
<t t-if="isSortingBy" t-set="isSortingBy" t-value="isSortingBy[0][1]"/>
|
|
<t t-else="" t-set="isSortingBy" t-value="website.shop_default_sort"/>
|
|
<h2 id="o_wsale_offcanvas_orderby_header" class="accordion-header mb-0">
|
|
<button class="o_wsale_offcanvas_title accordion-button rounded-0 collapsed"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
data-bs-target="#o_wsale_offcanvas_orderby"
|
|
aria-expanded="false"
|
|
aria-controls="o_wsale_offcanvas_orderby">
|
|
<b>Sort By</b>
|
|
</button>
|
|
</h2>
|
|
<div id="o_wsale_offcanvas_orderby"
|
|
class="accordion-collapse collapse"
|
|
aria-labelledby="o_wsale_offcanvas_orderby_header">
|
|
<div class="accordion-body pt-0">
|
|
<div class="list-group list-group-flush">
|
|
<a t-foreach="website_sale_sortable" t-as="sortby"
|
|
role="menuitem"
|
|
rel="noindex,nofollow"
|
|
t-att-href="keep(order=sortby[0])"
|
|
class="list-group-item border-0 ps-0 pb-0">
|
|
<div class="form-check d-inline-block">
|
|
<input type="radio"
|
|
t-attf-onclick="location.href='#{keep(order=sortby[0])}';"
|
|
class="form-check-input o_not_editable"
|
|
name="wsale_sortby_radios_offcanvas"
|
|
t-att-checked="isSortingBy and isSortingBy == sortby[1]">
|
|
<label class="form-check-label fw-normal" t-out="sortby[1]"/>
|
|
</input>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div t-if="opt_wsale_categories" class="accordion-item d-lg-none">
|
|
<h2 id="o_wsale_offcanvas_categories_header" class="accordion-header mb-0">
|
|
<button class="o_wsale_offcanvas_title accordion-button rounded-0 collapsed"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
data-bs-target="#o_wsale_offcanvas_categories"
|
|
aria-expanded="false"
|
|
aria-controls="o_wsale_offcanvas_categories">
|
|
<b>Categories</b>
|
|
</button>
|
|
</h2>
|
|
<div id="o_wsale_offcanvas_categories"
|
|
class="accordion-collapse collapse"
|
|
aria-labelledby="o_wsale_offcanvas_categories_header">
|
|
<div class="accordion-body pt-0">
|
|
<t t-call="website_sale.products_categories_list">
|
|
<t t-set="isOffcanvas" t-value="true"/>
|
|
<t t-set="_titleClasses" t-valuef="d-none"/>
|
|
<t t-set="_radioGroup" t-valuef="_offcanvas"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<t t-call="website_sale.product_attribute_filters_form">
|
|
<t t-set="isMobile" t-value="True"/>
|
|
</t>
|
|
<t t-if="opt_wsale_filter_price and (opt_wsale_attributes or opt_wsale_attributes_top)"
|
|
t-call="website_sale.filter_products_price">
|
|
<t
|
|
t-set="_classes"
|
|
t-valuef="o_wsale_offcanvas_title accordion accordion-flush px-4"
|
|
/>
|
|
<t t-set="_classes_title" t-valuef="ms-n1 pt-3 pb-2"/>
|
|
<t t-set="isOffcanvas" t-value="True"/>
|
|
</t>
|
|
</div>
|
|
<div class="offcanvas-body d-flex justify-content-between flex-grow-0 border-top overflow-hidden">
|
|
<a t-attf-class="btn btn-{{navClass}} d-flex py-1 mb-2 {{(not attrib_values and not isFilteringByPrice and not tags) and 'disabled' }}"
|
|
t-att-aria-disabled="(not attrib_values and not isFilteringByPrice and not tags) and 'true' or 'false'"
|
|
t-att-href="keep(attribute_values=0, tags=0, min_price=0, max_price=0)"
|
|
title="Clear Filters">
|
|
Clear Filters
|
|
</a>
|
|
</div>
|
|
</aside>
|
|
</template>
|
|
|
|
|
|
<!-- Top-Nav Categories -->
|
|
<template id="website_sale.filmstrip_categories" name="Categories Filmstrip">
|
|
<div
|
|
id="o_wsale_categories_filmstrip"
|
|
class="o_wsale_filmstrip_container o_wsale_filmstrip_default d-flex align-items-stretch overflow-hidden"
|
|
role="navigation"
|
|
>
|
|
<div class="o_wsale_filmstrip_wrapper position-relative overflow-auto z-1">
|
|
<ul
|
|
class="o_wsale_filmstrip d-flex align-items-stretch mb-0 list-unstyled overflow-visible"
|
|
role="menu"
|
|
aria-label="Categories"
|
|
>
|
|
<li
|
|
t-foreach="entries"
|
|
t-as="c"
|
|
t-att-data-publish="c.has_published_products and 'on' or 'off'"
|
|
role="presentation"
|
|
>
|
|
<a
|
|
t-att-href="keep('%s/category/%s' % (shop_path, slug(c)))"
|
|
class="o_wsale_filmstrip_link d-block w-100 h-100 text-decoration-none"
|
|
draggable="false"
|
|
role="menuitem"
|
|
t-att-aria-label="c.name"
|
|
>
|
|
<input
|
|
type="radio"
|
|
t-attf-name="wsale_categories_top_radios_{{parentCategoryId}}"
|
|
class="btn-check pe-none"
|
|
t-att-id="c.id"
|
|
t-att-value="c.id"
|
|
t-att-checked="'true' if c.id == category.id else None"
|
|
tabindex="-1"
|
|
/>
|
|
<div
|
|
t-att-class="'o_wsale_filmstrip_item position-relative d-flex w-100 h-100'
|
|
+ (c.id == category.id and ' active' or '')"
|
|
>
|
|
<div
|
|
t-if="c.image_128"
|
|
name="o_wsale_filmstrip_image"
|
|
class="o_wsale_filmstrip_image oe_img_bg o_bg_img_center rounded"
|
|
t-attf-style="background-image:url('data:image/png;base64, #{c.image_128}')"
|
|
t-att-alt="c.name"
|
|
role="img"
|
|
aria-label="Category image"
|
|
/>
|
|
<div
|
|
t-else=""
|
|
name="o_wsale_filmstrip_placeholder"
|
|
class="o_wsale_filmstrip_image o_wsale_filmstrip_placeholder oe_img_bg o_bg_img_center rounded"
|
|
role="img"
|
|
aria-label="Category image"
|
|
/>
|
|
<span t-field="c.name"/>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Categories Variations -->
|
|
<template
|
|
id="website_sale.filmstrip_categories_bordered"
|
|
inherit_id="website_sale.filmstrip_categories"
|
|
name="Categories Filmstrip Bordered"
|
|
active="False"
|
|
>
|
|
<div id="o_wsale_categories_filmstrip" position="attributes">
|
|
<attribute name="class" add="o_wsale_filmstrip_bordered" remove="o_wsale_filmstrip_default" separator=" "/>
|
|
</div>
|
|
</template>
|
|
<template
|
|
id="website_sale.filmstrip_categories_tabs"
|
|
inherit_id="website_sale.filmstrip_categories"
|
|
name="Categories Filmstrip Tabs"
|
|
active="False"
|
|
>
|
|
<div id="o_wsale_categories_filmstrip" position="attributes">
|
|
<attribute name="class" add="o_wsale_filmstrip_tabs" remove="o_wsale_filmstrip_default" separator=" "/>
|
|
</div>
|
|
</template>
|
|
<template
|
|
id="website_sale.filmstrip_categories_pills"
|
|
inherit_id="website_sale.filmstrip_categories"
|
|
name="Categories Filmstrip Tabs"
|
|
active="False"
|
|
>
|
|
<div id="o_wsale_categories_filmstrip" position="attributes">
|
|
<attribute name="class" add="o_wsale_filmstrip_pills" remove="o_wsale_filmstrip_default" separator=" "/>
|
|
</div>
|
|
</template>
|
|
<template
|
|
id="website_sale.filmstrip_categories_images"
|
|
inherit_id="website_sale.filmstrip_categories"
|
|
name="Categories Filmstrip Basic"
|
|
active="False"
|
|
>
|
|
<div id="o_wsale_categories_filmstrip" position="attributes">
|
|
<attribute name="class" add="o_wsale_filmstrip_images" remove="o_wsale_filmstrip_default" separator=" "/>
|
|
</div>
|
|
<div name="o_wsale_filmstrip_placeholder" position="attributes">
|
|
<attribute name="t-if">not c.has_published_products</attribute>
|
|
</div>
|
|
</template>
|
|
<template
|
|
id="website_sale.filmstrip_categories_grid"
|
|
inherit_id="website_sale.filmstrip_categories"
|
|
name="Categories Filmstrip Grid"
|
|
active="False"
|
|
>
|
|
<div id="o_wsale_categories_filmstrip" position="attributes">
|
|
<attribute name="class" add="o_wsale_filmstrip_grid" remove="o_wsale_filmstrip_default" separator=" "/>
|
|
</div>
|
|
</template>
|
|
<template
|
|
id="website_sale.filmstrip_categories_large_images"
|
|
inherit_id="website_sale.filmstrip_categories"
|
|
name="Categories Filmstrip Cover"
|
|
active="False"
|
|
>
|
|
<div id="o_wsale_categories_filmstrip" position="attributes">
|
|
<attribute name="class" add="o_wsale_filmstrip_large_images" remove="o_wsale_filmstrip_default" separator=" "/>
|
|
</div>
|
|
<div name="o_wsale_filmstrip_placeholder" position="attributes">
|
|
<attribute name="class" remove="o_wsale_filmstrip_placeholder" add="o_wsale_filmstrip_no_image" separator=" "/>
|
|
</div>
|
|
<div name="o_wsale_filmstrip_image" position="attributes">
|
|
<attribute name="t-attf-style">background-image:url('data:image/png;base64, #{c.image_512 if c.image_512 else c.image_128}')</attribute>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Floating Toolbar -->
|
|
<template
|
|
id="website_sale.floating_bar"
|
|
name="Shop Floating Bar"
|
|
inherit_id="website_sale.products"
|
|
active="False"
|
|
>
|
|
<xpath expr="//div[hasclass('o_wsale_products_main_row')]" position="attributes">
|
|
<attribute name="class" add="position-relative" separator=" "/>
|
|
</xpath>
|
|
|
|
<xpath expr="//div[@id='o_wsale_pager']" position="attributes">
|
|
<attribute name="t-attf-class" add="o_wsale_pager_accommodate_floating" separator=" "/>
|
|
</xpath>
|
|
|
|
<xpath expr="//div[@id='products_grid']" position="after">
|
|
<t
|
|
t-set="floatBar_hasSort"
|
|
t-value="is_view_active('website_sale.sort')"
|
|
/>
|
|
<t
|
|
t-set="floatBar_hasSearch"
|
|
t-value="is_view_active('website_sale.search')"
|
|
/>
|
|
<t
|
|
t-set="website_sale_pricelists"
|
|
t-value="website_sale_pricelists or website.get_pricelist_available(show_visible=True)"
|
|
/>
|
|
<t
|
|
t-set="floatBar_hasPricelist"
|
|
t-value="website_sale_pricelists and len(website_sale_pricelists)>1"
|
|
/>
|
|
<div
|
|
id="o_wsale_floating_bar_rail"
|
|
class="o_not_editable position-absolute top-0 bottom-0 d-flex justify-content-end align-items-end p-0"
|
|
>
|
|
<nav
|
|
id="o_wsale_floating_bar"
|
|
t-attf-class="navbar position-sticky d-flex gap-1 gap-lg-2 flex-shrink-0 bg-body rounded mb-2 px-2 px-lg-3 shadow {{floatBar_hasSearch and 'ps-1 ps-lg-1'}}"
|
|
>
|
|
<div t-if="floatBar_hasSearch">
|
|
<a
|
|
t-attf-class="btn o_not_editable {{(wsale_has_filters_btn or floatBar_hasSort or floatBar_hasPricelist) and 'me-n2'}}"
|
|
data-bs-target="#o_wsale_search_modal"
|
|
data-bs-toggle="modal"
|
|
role="button"
|
|
title="Search Products"
|
|
href="#"
|
|
>
|
|
<i class="oi oi-search" role="img"/>
|
|
</a>
|
|
</div>
|
|
<span
|
|
t-if="floatBar_hasSearch and (wsale_has_filters_btn or floatBar_hasSort or floatBar_hasPricelist)"
|
|
t-attf-class="align-self-stretch border-start opacity-50 {{(not floatBar_hasSort and not floatBar_hasPricelist and not wsale_has_filters_btn_lg) and 'd-lg-none'}}"
|
|
/>
|
|
<t t-if="floatBar_hasPricelist" t-call="website_sale.pricelist_list"/>
|
|
<t
|
|
t-if="floatBar_hasSort"
|
|
t-call="website_sale.sort"
|
|
/>
|
|
<button
|
|
t-if="wsale_has_filters_btn"
|
|
t-attf-class="btn btn-{{navClass}} position-relative {{not wsale_has_filters_btn_lg and 'd-lg-none'}}"
|
|
data-bs-toggle="offcanvas"
|
|
data-bs-target="#o_wsale_offcanvas"
|
|
>
|
|
<i class="oi oi-settings-adjust" role="img"/>
|
|
<span
|
|
t-if="isFilteringByPrice or attrib_set or tags"
|
|
t-attf-class="position-absolute top-0 start-100 translate-middle rounded-circle bg-danger p-1"
|
|
>
|
|
<span class="visually-hidden">filters active</span>
|
|
</span>
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Add to cart button-->
|
|
<template id="categories_recursive" name="Category list">
|
|
<li class="nav-item mb-1" t-att-data-publish="c.has_published_products and 'on' or 'off'">
|
|
<t t-call="website_sale.categorie_link"/>
|
|
<!-- Dynamic category display based on search -->
|
|
<t t-set="children" t-value="not search and c.child_id or c.child_id.filtered(lambda c: c.id in search_categories_ids)"/>
|
|
<!-- Only show categories with published products to portal users preventing from access errors -->
|
|
<t t-set="children" t-value="request.env.user._is_internal() and children or children.filtered('has_published_products')"/>
|
|
|
|
<ul t-if="children" class="nav flex-column nav-hierarchy mt-1 ps-3">
|
|
<t t-foreach="children" t-as="c">
|
|
<t t-if="not search or c.id in search_categories_ids">
|
|
<t t-call="website_sale.categories_recursive" />
|
|
</t>
|
|
</t>
|
|
</ul>
|
|
</li>
|
|
</template>
|
|
|
|
<template id="categorie_link" name="Category Link">
|
|
<a
|
|
t-att-href="keep('%s/category/%s' % (shop_path, slug(c)))"
|
|
t-attf-class="{{c.id == category.id and 'text-decoration-underline'}} p-0"
|
|
t-field="c.name"
|
|
/>
|
|
</template>
|
|
|
|
<template id="website_sale.products_categories_list" active="True" name="eCommerce Categories">
|
|
<!--
|
|
We must keep `t-attf-class` attr. for both following divs to work correctly with
|
|
products_categories_list_collapsible and option_collapse_products_categories
|
|
-->
|
|
<div t-attf-class="d-contents">
|
|
<h6 t-attf-class="o_categories_collapse_title {{_titleClasses}}"><b>Categories</b></h6>
|
|
<div name="wsale_products_categories_list" t-attf-class="wsale_products_categories_list">
|
|
<ul class="nav d-flex flex-column mb-3">
|
|
<li class="nav-item mb-1">
|
|
<a
|
|
t-att-href="keep(shop_path)"
|
|
t-attf-class="{{not category and 'text-decoration-underline'}} p-0"
|
|
>
|
|
All Products
|
|
</a>
|
|
</li>
|
|
<t t-foreach="categories" t-as="c">
|
|
<t t-call="website_sale.categories_recursive"/>
|
|
</t>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.products_categories_list_collapsible"
|
|
inherit_id="website_sale.products_categories_list"
|
|
name="Collapsed eCommerce Categories"
|
|
>
|
|
<xpath expr="//div" position="attributes">
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="{{not isOffcanvas and 'accordion-item'}}"
|
|
remove="d-contents"
|
|
separator=" "
|
|
/>
|
|
</xpath>
|
|
<xpath expr="//h6" position="attributes">
|
|
<attribute name="t-attf-class" add="accordion-header" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//h6//b" position="replace">
|
|
<button
|
|
class="accordion-button px-0 bg-transparent shadow-none"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
t-attf-data-bs-target="#o_wsale_categories"
|
|
t-attf-aria-controls="o_wsale_categories"
|
|
aria-expanded="true"
|
|
>
|
|
<b>Categories</b>
|
|
</button>
|
|
</xpath>
|
|
|
|
<xpath expr="//div//div" position="attributes">
|
|
<attribute
|
|
name="t-attf-id"
|
|
add="{{not isOffcanvas and 'o_wsale_categories'}}"
|
|
separator=" "
|
|
/>
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="{{not isOffcanvas and 'accordion-collapse collapse show'}}"
|
|
separator=" "
|
|
/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="option_collapse_categories_recursive" name="Collapse Category Recursive">
|
|
<!-- Dynamic category display based on search -->
|
|
<t t-set="children" t-value="not search and c.child_id or c.child_id.filtered(lambda c: c.id in search_categories_ids)"/>
|
|
<!-- Only show categories with published products to portal users preventing from access errors -->
|
|
<t t-set="children" t-value="request.env.user._is_internal() and children or children.filtered('has_published_products')"/>
|
|
|
|
<t t-if="children">
|
|
<t t-set="isOpen" t-value="c.id in category.parents_and_self.ids"/>
|
|
|
|
<li class="nav-item" t-att-data-publish="c.has_published_products and 'on' or 'off'">
|
|
<div class="accordion-header d-flex mb-1">
|
|
<t t-call="website_sale.categorie_link"/>
|
|
<button t-attf-id="o_wsale_cat_accordion_title_{{c.id}}"
|
|
t-attf-class="o_categories_recursive_button accordion-button p-0 ms-3 {{not isOpen and 'collapsed'}} w-auto flex-grow-1 bg-transparent shadow-none"
|
|
t-attf-data-bs-target="#o_wsale_cat_accordion_{{c.id}}"
|
|
t-att-aria-expanded="isOpen and 'true' or 'false'"
|
|
t-attf-aria-controls="o_wsale_cat_accordion_{{c.id}}"
|
|
data-bs-toggle="collapse"
|
|
type="button"/>
|
|
</div>
|
|
<ul t-attf-id="o_wsale_cat_accordion_{{c.id}}"
|
|
t-attf-class="accordion-collapse list-unstyled ps-2 collapse {{isOpen and 'show'}}"
|
|
t-attf-aria-labelledby="o_wsale_cat_accordion_title_{{c.id}}">
|
|
<t t-set="parentCategoryId" t-value="c.id"/>
|
|
<t t-if="isOffcanvas" t-set="parentCategoryId" t-valuef="offcanvas_{{c.id}}"/>
|
|
|
|
<t t-foreach="children" t-as="c">
|
|
<t t-call="website_sale.option_collapse_categories_recursive"/>
|
|
</t>
|
|
</ul>
|
|
</li>
|
|
</t>
|
|
|
|
<li t-else="" class="nav-item mb-1"
|
|
t-att-data-publish="c.has_published_products and 'on' or 'off'">
|
|
<t t-if="isOffcanvas" t-set="parentCategoryId" t-valuef="offcanvas"/>
|
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
|
<t t-call="website_sale.categorie_link"/>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
|
|
<template id="option_collapse_products_categories" name="Collapsible Category List" inherit_id="website_sale.products_categories_list" active="False">
|
|
<xpath expr="//t[@t-call='website_sale.categories_recursive']" position="attributes">
|
|
<attribute name="t-call">website_sale.option_collapse_categories_recursive</attribute>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="products_attributes" inherit_id="website_sale.products" active="True" name="Attributes & Variants filters">
|
|
<xpath expr="//div[contains(@t-attf-class, 'products_attributes_filters')]" position="inside">
|
|
<div t-if="attributes or all_tags" id="wsale_products_attributes_collapse"
|
|
class=" position-relative">
|
|
<t t-call="website_sale.product_attribute_filters_form">
|
|
<t t-set="isMobile" t-value="False"/>
|
|
</t>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="products_attributes_collapsible"
|
|
name="Collapsed Attributes & Variants filters"
|
|
inherit_id="website_sale.product_attribute_filters_form"
|
|
>
|
|
<xpath expr="//form" position="attributes">
|
|
<t t-if="not isMobile">
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="wsale_accordion_collapsible accordion accordion-flush"
|
|
remove="mb-2"
|
|
separator=" "
|
|
/>
|
|
</t>
|
|
</xpath>
|
|
<xpath expr="//div[contains(@t-attf-class, 'accordion-item')]//h6" position="replace">
|
|
<div t-if="not isMobile" class="accordion-header h6 mb-0">
|
|
<button
|
|
t-attf-class="accordion-button px-0 bg-transparent shadow-none {{'' if _expand_attributes else 'collapsed'}}"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
t-attf-data-bs-target="#o_products_attributes_{{a.id}}"
|
|
t-attf-aria-controls="o_products_attributes_{{a.id}}"
|
|
t-att-aria-expanded="_expand_attributes"
|
|
>
|
|
<b t-field="a.name"/>
|
|
</button>
|
|
</div>
|
|
</xpath>
|
|
<xpath expr="//div[contains(@t-attf-id, 'o_products_attributes_')]" position="attributes">
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="accordion-collapse collapse {{'show' if _expand_attributes else ''}}"
|
|
separator=" "
|
|
/>
|
|
<attribute name="data-bs-parent" add="wsale_products_attributes_collapse"/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="filter_products_price" name="Filter by Prices">
|
|
<t t-set="is_disabled" t-value="available_min_price == available_max_price"/>
|
|
<div
|
|
id="o_wsale_price_range_option"
|
|
t-attf-class="position-relative {{_classes}} {{is_disabled and 'opacity-75 pe-none user-select-none'}}"
|
|
>
|
|
<t t-if="is_sidebar_collapsible">
|
|
<div class="accordion-item">
|
|
<div class="accordion-header h6 mb-0">
|
|
<button
|
|
class="accordion-button px-0 bg-transparent shadow-none"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
t-attf-data-bs-target="#o_wsale_price_range_option_inner"
|
|
t-attf-aria-controls="o_wsale_price_range_option_inner"
|
|
aria-expanded="true"
|
|
>
|
|
<b>Price Range</b>
|
|
</button>
|
|
</div>
|
|
<div
|
|
id="o_wsale_price_range_option_inner"
|
|
class="accordion-collapse collapse show"
|
|
>
|
|
<input
|
|
type="range"
|
|
multiple="multiple"
|
|
t-attf-class="form-range range-with-input {{_classes_input}}"
|
|
t-att-data-currency="website.currency_id.symbol"
|
|
t-att-data-currency-position="website.currency_id.position"
|
|
t-att-data-url="keep(min_price=0, max_price=0)"
|
|
t-att-step="website.currency_id.rounding"
|
|
t-att-min="'%f' % (available_min_price)"
|
|
t-att-max="'%f' % (available_max_price)"
|
|
t-att-value="'%f,%f' % (min_price, max_price)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<t t-else="">
|
|
<label t-attf-class="h6 m-0 {{_classes_title}}">
|
|
<b>Price Range</b>
|
|
</label>
|
|
<input
|
|
type="range"
|
|
multiple="multiple"
|
|
t-attf-class="form-range range-with-input {{_classes_input}}"
|
|
t-att-data-currency="website.currency_id.symbol"
|
|
t-att-data-currency-position="website.currency_id.position"
|
|
t-att-data-url="keep(min_price=0, max_price=0)"
|
|
t-att-step="website.currency_id.rounding"
|
|
t-att-min="'%f' % (available_min_price)"
|
|
t-att-max="'%f' % (available_max_price)"
|
|
t-att-value="'%f,%f' % (min_price, max_price)"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="filter_products_tags" name="Filter by Tags" active="True">
|
|
<div t-if="all_tags" class="accordion-item">
|
|
<div class="accordion-header h6 mb-0">
|
|
<button class="accordion-button px-0 bg-transparent shadow-none"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
aria-expanded="true"
|
|
t-attf-data-bs-target="#o_wsale_tags_option_inner"
|
|
t-attf-aria-controls="o_wsale_tags_option_inner">
|
|
<b>Tags</b>
|
|
</button>
|
|
</div>
|
|
<div id="o_wsale_tags_option_inner" class="accordion-collapse collapse show">
|
|
<div class="flex-column mb-3">
|
|
<t t-call="website_sale.filter_products_tags_list">
|
|
<t t-set="all_tags" t-value="all_tags"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="filter_products_tags_list">
|
|
<t t-foreach="all_tags" t-as="tag" class="list-group-item border-0 ps-0 pb-0">
|
|
<div class="form-check mb-1">
|
|
<input type="checkbox"
|
|
name="tags"
|
|
class="form-check-input"
|
|
t-attf-id="tag_#{tag.id}"
|
|
t-att-value="tag.id"
|
|
t-att-checked="'checked' if tag.id in tags else None"
|
|
/>
|
|
<label class="form-check-label fw-normal" t-attf-for="tag_#{tag.id}" t-field="tag.name"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="products_list_view" inherit_id="website_sale.products" active="False" name="List View (by default)">
|
|
<xpath expr="//div[@id='products_grid']" position="after">
|
|
<!-- Nothing to do, this view is only meant to allow the server -->
|
|
<!-- to know if the list view layout should be used -->
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Enable HTML previews in edition mode. The template is active by default, but can be
|
|
manually disabled for advanced debug if necessary -->
|
|
<template id="website_sale.editor_previews" name="Enable edit previews" inherit_id="website_sale.products">
|
|
<xpath expr="//div[@id='wrap']" position="before">
|
|
<t t-set="editionPreviews" t-value="true" groups="website.group_website_designer"/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- /shop/product page -->
|
|
<template id="base_unit_price" name="Product Base unit price">
|
|
<span
|
|
class="o_base_unit_price"
|
|
t-out="base_unit_price"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
/>
|
|
/ <span class="oe_custom_base_unit" t-field="product.base_unit_name"/>
|
|
</template>
|
|
|
|
<template id="website_sale.product" name="Product" track="1">
|
|
<!-- Qweb variable defining the class suffix for navbar items.
|
|
Change accordingly to the derired visual result (eg. `primary`, `dark`...)-->
|
|
<t t-set="navClass" t-valuef="light"/>
|
|
|
|
<t
|
|
t-set="is_fullwidth_content"
|
|
t-value="website.product_page_image_width in ['none', '100_pc']"
|
|
/>
|
|
<t t-set="is_50_pc" t-value="website.product_page_image_width == '50_pc'"/>
|
|
|
|
<t t-call="website.layout">
|
|
<t t-set="additional_title" t-value="product.name" />
|
|
<div id="wrap" class="js_sale o_wsale_product_page">
|
|
<div class="oe_structure oe_empty oe_structure_not_nearest" id="oe_structure_website_sale_product_1" data-editor-message.translate="DROP BUILDING BLOCKS HERE TO MAKE THEM AVAILABLE ACROSS ALL PRODUCTS"/>
|
|
<section id="product_detail"
|
|
t-attf-class="oe_website_sale mt-1 mt-lg-2 mb-5 #{'discount'
|
|
if combination_info['has_discounted_price'] else ''}
|
|
{{website._get_product_page_container() == 'regular' and 'o_wsale_container_lg container-lg' or (website._get_product_page_container() == 'fluid' and 'o_wsale_container_fluid container-fluid' or false)}}
|
|
o_wsale_product_page_opt_image_width_{{website.product_page_image_width}}
|
|
o_wsale_product_page_opt_image_ratio_{{website.product_page_image_ratio}}
|
|
o_wsale_product_page_opt_image_ratio_mobile_{{website.product_page_image_ratio_mobile}}
|
|
o_wsale_product_page_opt_image_radius_{{website.product_page_image_roundness}}
|
|
"
|
|
t-att-data-image-ratio="website.product_page_image_ratio"
|
|
t-att-data-view-track="view_track and '1' or '0'"
|
|
>
|
|
<div class="o_wsale_content_contained container d-flex flex-wrap align-items-center py-3">
|
|
<div class="d-flex align-items-center flex-grow-1">
|
|
<ol class="o_wsale_breadcrumb breadcrumb m-0 p-0">
|
|
<li class="breadcrumb-item d-none d-lg-inline-block">
|
|
<a t-att-href="keep(shop_path)">
|
|
<t t-call="website_sale.all_products_link_name"/>
|
|
</a>
|
|
</li>
|
|
<t t-foreach="category.parents_and_self" t-as="cat">
|
|
<li
|
|
class="breadcrumb-item d-none d-lg-inline-block"
|
|
>
|
|
<a
|
|
class="py-2 py-lg-0"
|
|
t-att-href="keep('%s/category/%s' % (shop_path, slug(cat)))"
|
|
>
|
|
<i
|
|
class="oi oi-chevron-left d-lg-none me-1"
|
|
role="img"
|
|
/>
|
|
<t t-out="cat.name"/>
|
|
</a>
|
|
</li>
|
|
</t>
|
|
<li class="breadcrumb-item d-none d-lg-inline-block active">
|
|
<span t-field="product.name" />
|
|
</li>
|
|
<li class="breadcrumb-item d-lg-none">
|
|
<a t-att-href="category and keep('%s/category/%s' % (shop_path, slug(cat))) or keep(shop_path)">
|
|
<i class="oi oi-chevron-left me-2" role="img"/>
|
|
<t t-if="category.name" t-out="category.name"/>
|
|
<t t-else="" t-call="website_sale.all_products_link_name"/>
|
|
</a>
|
|
</li>
|
|
</ol>
|
|
<div class="d-flex d-md-none gap-2 ms-auto">
|
|
<t t-call="website_sale.pricelist_list"/>
|
|
<a
|
|
t-attf-class="o_wsale_product_search_mobile_btn o_not_editable btn btn-{{navClass}} d-none"
|
|
data-bs-target="#o_wsale_product_search_modal"
|
|
data-bs-toggle="modal"
|
|
role="button"
|
|
title="Search Products"
|
|
href="#"
|
|
>
|
|
<i class="oi oi-search" role="presentation"/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="o_wsale_product_top_bar_desktop d-none d-md-inline-flex flex-wrap gap-2 w-auto mb-lg-auto"
|
|
>
|
|
<t t-call="website_sale.pricelist_list"/>
|
|
</div>
|
|
</div>
|
|
<div
|
|
t-attf-class="row align-items-lg-start {{website.product_page_cols_order == 'inverse' and 'flex-lg-row-reverse' or ''}}"
|
|
id="product_detail_main"
|
|
data-name="Product Page"
|
|
t-att-data-image_layout="website.product_page_image_layout"
|
|
>
|
|
<div
|
|
t-attf-class="o_wsale_product_images col o_wsale_sticky_object"
|
|
t-att-data-image-amount="len(product_variant._get_images() if product_variant else product._get_images())"
|
|
>
|
|
<t t-call="website_sale.shop_product_images"/>
|
|
</div>
|
|
<div
|
|
id="product_details"
|
|
t-attf-class="position-relative position-lg-sticky o_wsale_sticky_object col"
|
|
>
|
|
<div id="o_wsale_product_details_content" t-attf-class="js_product js_main_product o_wsale_content_contained container">
|
|
|
|
<!-- Product title (purchase not available)-->
|
|
<t t-set="is_add_to_cart_possible" t-value="product._is_add_to_cart_possible()"/>
|
|
<t
|
|
t-if="not is_add_to_cart_possible"
|
|
t-call="website_sale.product_title"
|
|
/>
|
|
|
|
<form t-if="is_add_to_cart_possible">
|
|
<!-- Product title (purchase available)-->
|
|
<t t-call="website_sale.product_title"/>
|
|
|
|
<div
|
|
t-if="not is_view_active('website_sale.cta_wrapper_boxed')"
|
|
class="o_wsale_product_details_content_section o_wsale_product_details_content_section_price mb-4"
|
|
>
|
|
<t t-call="website_sale.product_price"/>
|
|
<small t-if="combination_info.get('base_unit_price')"
|
|
class="ms-1 text-muted o_base_unit_price_wrapper d-none">
|
|
<t t-call="website_sale.base_unit_price">
|
|
<t
|
|
t-set="base_unit_price"
|
|
t-value="combination_info['base_unit_price']"
|
|
/>
|
|
</t>
|
|
</small>
|
|
</div>
|
|
|
|
<input
|
|
type="hidden"
|
|
name="csrf_token"
|
|
t-att-value="request.csrf_token()"
|
|
/>
|
|
|
|
<t name="variant_info">
|
|
<input
|
|
type="hidden"
|
|
class="o_not_editable product_id"
|
|
name="product_id"
|
|
t-att-value="product_variant.id"
|
|
/>
|
|
<input
|
|
type="hidden"
|
|
class="o_not_editable product_template_id"
|
|
name="product_template_id"
|
|
t-att-value="product.id"
|
|
/>
|
|
<input
|
|
t-if="product.public_categ_ids.ids"
|
|
type="hidden"
|
|
class="product_category_id"
|
|
name="product_category_id"
|
|
t-att-value="product.public_categ_ids.ids[0]"
|
|
/>
|
|
<input
|
|
type="hidden"
|
|
name="product_type"
|
|
t-att-value="product.type"
|
|
/>
|
|
<div
|
|
t-attf-class="o_wsale_product_details_content_section o_wsale_product_details_content_section_attributes mb-4 {{not (product.valid_product_template_attribute_line_ids or product._has_multiple_uoms()) and 'd-none' or ''}}"
|
|
>
|
|
<t t-call="website_sale.variants">
|
|
<t t-set="ul_class" t-valuef="flex-column" />
|
|
<t t-set="parent_combination" t-value="None" />
|
|
<t
|
|
t-set="combination"
|
|
t-value="combination_info['combination']"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
<div class="o_wsale_product_details_content_section o_wsale_product_details_content_section_cta mb-4">
|
|
<t
|
|
t-call="website_sale.cta_wrapper"
|
|
/>
|
|
<t
|
|
t-if="is_view_active('website_sale.product_terms_and_conditions')"
|
|
t-call="website_sale.product_terms_and_conditions"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
class="o_wsale_product_details_content_section o_wsale_product_details_content_section_accordion mb-4"
|
|
t-if="is_view_active('website_sale_comparison.accordion_specs_item') or is_view_active('website_sale.accordion_more_information')"
|
|
>
|
|
<t t-call="website_sale.product_accordion"/>
|
|
</div>
|
|
|
|
<div
|
|
class="o_wsale_product_details_content_section o_wsale_product_details_content_section_contact mb-4"
|
|
>
|
|
<div
|
|
id="contact_us_wrapper"
|
|
t-attf-class="d-flex oe_structure oe_structure_solo #{_div_classes} {{'d-flex' if combination_info['prevent_zero_price_sale'] else 'd-none'}}"
|
|
>
|
|
<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"
|
|
t-att-data-url="website.contact_us_button_url"
|
|
class="btn btn-primary btn_cta"
|
|
>
|
|
Contact Us
|
|
</a>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
<t
|
|
t-set="single_value_attributes"
|
|
t-value="product.valid_product_template_attribute_line_ids._prepare_single_value_for_display()"
|
|
/>
|
|
<div
|
|
id="product_attributes_simple"
|
|
t-if="single_value_attributes"
|
|
class="o_wsale_product_details_content_section o_wsale_product_details_content_section_specs mb-4"
|
|
>
|
|
<table t-attf-class="table table-sm text-muted {{'' if single_value_attributes else 'd-none'}}">
|
|
<t t-foreach="single_value_attributes" t-as="attribute">
|
|
<tr>
|
|
<td>
|
|
<span t-field="attribute.name"/>:
|
|
<t
|
|
t-foreach="single_value_attributes[attribute]"
|
|
t-as="ptal"
|
|
>
|
|
<span t-field="ptal.product_template_value_ids._only_active().name"/>
|
|
<t t-if="not ptal_last">, </t>
|
|
</t>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</table>
|
|
</div>
|
|
<t t-set="product_documents" t-value="product.sudo().product_document_ids.filtered(lambda doc: doc.shown_on_product_page)"/>
|
|
<div
|
|
id="product_documents"
|
|
class="o_wsale_product_details_content_section o_wsale_product_details_content_section_documents mb-4"
|
|
t-if="product_documents"
|
|
>
|
|
<strong>Documents</strong>
|
|
<div class="list-group">
|
|
<t t-foreach="product_documents" t-as="document_sudo">
|
|
<t
|
|
t-set="attachment_sudo"
|
|
t-value="document_sudo.ir_attachment_id"
|
|
/>
|
|
<t
|
|
t-set="target"
|
|
t-value="attachment_sudo.type == 'url' and '_blank' or '_self'"
|
|
/>
|
|
<t
|
|
t-set="icon"
|
|
t-value="attachment_sudo.type == 'url' and 'fa-link' or 'fa-download'"
|
|
/>
|
|
<a
|
|
class="list-group-item list-group-item-action"
|
|
t-att-href="'/shop/' + slug(product) + '/document/' + str(document_sudo.id)"
|
|
t-att-target="target">
|
|
<i t-att-class="'fa ' + icon + ' me-2'"/>
|
|
<t t-out="attachment_sudo.name"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<div
|
|
id="product_full_description"
|
|
t-field="product.website_description"
|
|
class="oe_structure oe_empty mt16"
|
|
/>
|
|
<div class="oe_structure oe_empty oe_structure_not_nearest mt16" id="oe_structure_website_sale_product_2" data-editor-message.translate="DROP BUILDING BLOCKS HERE TO MAKE THEM AVAILABLE ACROSS ALL PRODUCTS"/>
|
|
</div>
|
|
<div
|
|
id="o_wsale_product_search_modal"
|
|
class="modal fade css_editable_mode_hidden"
|
|
aria-hidden="true"
|
|
tabindex="-1"
|
|
>
|
|
<div class="modal-dialog modal-lg pt-5">
|
|
<div class="modal-content mt-5 bg-transparent border-0">
|
|
<div class="o_container_small">
|
|
<t t-call="website.website_search_box_input">
|
|
<t t-set="default_style" t-valuef="true"/>
|
|
<t t-set="search_type" t-valuef="products"/>
|
|
<t t-set="_classes" t-valuef="input-group rounded o_cc o_cc1 mb-1"/>
|
|
<t t-set="_input_classes" t-valuef="border-0 p-3"/>
|
|
<t t-set="_submit_classes" t-valuef="px-4"/>
|
|
<t t-if="category" t-set="placeholder">Search in <t t-out="category.name"/></t>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="website_sale.product_title">
|
|
<div
|
|
t-attf-class="o_wsale_product_details_content_section o_wsale_product_details_content_section_title {{'mb-3' if not product.description_ecommerce else ''}}"
|
|
>
|
|
<!-- FIXME VCR variable seems unused -->
|
|
<t t-set="base_url" t-value="website.get_base_url()"/>
|
|
<h1
|
|
t-attf-class="h3 {{'mb-3' if not product.description_ecommerce and not is_view_active('website_sale.product_comment') else ''}}"
|
|
t-field="product.name"
|
|
>
|
|
Product Name
|
|
</h1>
|
|
<t t-if="is_view_active('website_sale.product_comment')">
|
|
<a
|
|
href="#o_product_page_reviews"
|
|
class="o_product_page_reviews_link d-block mb-2 text-decoration-none"
|
|
>
|
|
<t t-call="portal_rating.rating_widget_stars_static">
|
|
<!-- sudo: product.product - visitor can access product average rating -->
|
|
<t t-set="rating_avg" t-value="product.sudo().rating_avg"/>
|
|
<t t-set="trans_text_plural">%s reviews</t>
|
|
<t t-set="trans_text_singular">%s review</t>
|
|
<t
|
|
t-set="rating_count"
|
|
t-value="(trans_text_plural if product.rating_count > 1 else trans_text_singular) % product.rating_count"
|
|
/>
|
|
</t>
|
|
</a>
|
|
</t>
|
|
<div
|
|
t-field="product.description_ecommerce"
|
|
t-attf-class="oe_structure mb-3 text-muted"
|
|
placeholder="A detailed, formatted description to promote your product on this page. Use '/' to discover more features."
|
|
/>
|
|
<t
|
|
t-if="is_view_active('website_sale.product_tags')"
|
|
t-call="website_sale.product_tags"
|
|
>
|
|
<t
|
|
t-set="all_product_tags"
|
|
t-value="product_variant.all_product_tag_ids"
|
|
/>
|
|
</t>
|
|
<p t-if="(not is_add_to_cart_possible) and (not product.active)" class="alert alert-warning">
|
|
This product is no longer available.
|
|
</p>
|
|
<p t-elif="not is_add_to_cart_possible" class="alert alert-warning">
|
|
This product has no valid combination.
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.cta_wrapper" name="CTA Wrapper">
|
|
<div
|
|
id="o_wsale_cta_wrapper"
|
|
t-att-class="'d-flex flex-wrap align-items-center gap-2 mb-4' +
|
|
(' w-100 w-lg-auto' if is_50_pc else ' w-100') +
|
|
(' ' if not is_fullwidth_content and not is_view_active('website_sale.cta_wrapper_boxed') else ' ')"
|
|
>
|
|
<div
|
|
id="add_to_cart_wrap"
|
|
t-attf-class="{{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-flex'}} flex-grow-1 flex-lg-grow-0 flex-wrap align-items-center gap-2 {{'' if is_50_pc else ' w-100'}}"
|
|
>
|
|
<a
|
|
id="add_to_cart"
|
|
role="button"
|
|
href="#"
|
|
t-attf-class="btn btn-primary js_check_product a-submit flex-grow-1"
|
|
t-att-data-show-quantity="is_view_active('website_sale.product_quantity')"
|
|
data-animation-selector=".o_wsale_product_images"
|
|
>
|
|
<i class="fa fa-shopping-cart me-2"/>
|
|
Add to cart
|
|
</a>
|
|
</div>
|
|
<div
|
|
id="product_option_block"
|
|
t-attf-class="d-flex d-empty-none flex-wrap gap-2 w-100 {{'w-sm-auto' if is_50_pc and not (is_view_active('website_sale.cta_wrapper_boxed') or is_view_active('website_sale.cta_wrapper_large')) else ''}}"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.cta_wrapper_boxed"
|
|
inherit_id="website_sale.cta_wrapper"
|
|
active="False"
|
|
name="Bordered CTA Wrapper"
|
|
>
|
|
<xpath expr="//div[@id='o_wsale_cta_wrapper']" position="attributes">
|
|
<attribute
|
|
name="t-att-class"
|
|
remove="pt-3"
|
|
add="' o_wsale_cta_wrapper_boxed border rounded p-3'"
|
|
separator=" + "
|
|
/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='add_to_cart_wrap']" position="attributes">
|
|
<attribute name="t-attf-class" remove="flex-lg-grow-0" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='product_option_block']" position="attributes">
|
|
<attribute name="t-att-class" remove="flex-lg-grow-0" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='o_wsale_cta_wrapper']" position="inside">
|
|
<div class="d-flex gap-5 flex-grow-1 justify-content-between align-items-center order-first w-100 mb-3 pb-3 border-bottom">
|
|
<span name="o_wsale_cta_wrapper_boxed_price_label" class="text-muted">Price</span>
|
|
<t t-call="website_sale.product_price"/>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.cta_wrapper_large"
|
|
inherit_id="website_sale.cta_wrapper"
|
|
active="False"
|
|
name="Large CTA Wrapper">
|
|
<xpath expr="//div[@id='o_wsale_cta_wrapper']" position="attributes">
|
|
<attribute name="t-att-class" add="' o_wsale_cta_wrapper_large'" separator=" + "/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='add_to_cart_wrap']" position="attributes">
|
|
<attribute name="t-attf-class" remove="flex-lg-grow-0" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='product_option_block']" position="attributes">
|
|
<attribute name="t-att-class" remove="flex-lg-grow-0" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//a[@id='add_to_cart']" position="attributes">
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="btn-lg {{('' if is_50_pc else ' px-xl-0')}}"
|
|
separator=" "
|
|
/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.cta_separator"
|
|
inherit_id="website_sale.product"
|
|
name="Product Info - Dividers">
|
|
<xpath expr="//section[@id='product_detail']" position="attributes">
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="o_wsale_product_page_opt_separators"
|
|
separator=" "
|
|
/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.product_search"
|
|
inherit_id="website_sale.product"
|
|
active="False"
|
|
name="Searchbar On Product Page"
|
|
>
|
|
<xpath expr="//div[hasclass('o_wsale_product_top_bar_desktop')]" position="inside">
|
|
<t t-call="website_sale.search">
|
|
<t t-set="search" t-value="False"/>
|
|
<t t-set="_classes" t-value="'me-sm-2'"/>
|
|
</t>
|
|
</xpath>
|
|
<xpath expr="//a[contains(@t-attf-class, 'o_wsale_product_search_mobile_btn')]" position="attributes">
|
|
<attribute name="t-attf-class" add="d-md-none" remove="d-none" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="all_products_link_name" name="All Products name">
|
|
<span placeholder="All products">All products</span>
|
|
</template>
|
|
|
|
<template id="product_accordion" name="Accordion On Product Page">
|
|
<div
|
|
t-if="is_view_active('website_sale_comparison.accordion_specs_item')
|
|
or is_view_active('website_sale.accordion_more_information')"
|
|
id="product_accordion"
|
|
class="o_accordion_not_initialized accordion accordion-flush my-4"
|
|
>
|
|
<div
|
|
t-if="is_view_active('website_sale.accordion_more_information')"
|
|
id="more_information_accordion_item"
|
|
class="accordion-item"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<template
|
|
id="accordion_more_information"
|
|
name="More Information Accordion Item"
|
|
inherit_id="website_sale.product_accordion"
|
|
active="False"
|
|
>
|
|
<xpath expr="//div[@id='more_information_accordion_item']" position="inside">
|
|
<div class="accordion-header my-0 h6">
|
|
<button
|
|
class="accordion-button collapsed fw-medium"
|
|
type="button"
|
|
data-bs-toggle="collapse"
|
|
data-bs-target="#customizable_item"
|
|
aria-expanded="false"
|
|
aria-controls="customizable_item"
|
|
>
|
|
More Information
|
|
</button>
|
|
</div>
|
|
<div
|
|
id="customizable_item"
|
|
class="accordion-collapse collapse"
|
|
data-bs-parent="#product_accordion"
|
|
>
|
|
<div class="accordion-body py-0">
|
|
<p>This content will be shared across all product pages.</p>
|
|
</div>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="website_sale.product_tags" name="Product Tags" active="True">
|
|
<t t-set="visible_tags" t-value="all_product_tags.filtered('visible_to_customers')"/>
|
|
<div
|
|
t-attf-class="o_product_tags o_field_tags {{ 'd-flex flex-wrap align-items-center gap-2 mb-2 mt-1' if visible_tags else '' }}"
|
|
>
|
|
<t t-foreach="visible_tags" t-as="tag">
|
|
<span t-if="tag.image"
|
|
class="order-0"
|
|
t-field="tag.image"
|
|
t-options="{'widget': 'image', 'class': 'o_product_tag_img rounded'}"
|
|
/>
|
|
<span t-else="" class="position-relative order-1 py-1 px-2">
|
|
<span class="position-absolute top-0 start-0 w-100 h-100 rounded"
|
|
t-attf-style="background-color: #{tag.color}; opacity: .2;"
|
|
/>
|
|
<span class="text-nowrap small"
|
|
t-attf-style="color: #{tag.color}"
|
|
t-field="tag.name"
|
|
/>
|
|
</span>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="alternative_products" name="Alternative Products" inherit_id="website_sale.product" active="True">
|
|
<div t-field="product.website_description" position="after">
|
|
<div class="oe_structure oe_structure_solo oe_unremovable oe_unmovable" id="oe_structure_website_sale_recommended_products" t-ignore="true" t-if="product.alternative_product_ids">
|
|
<section data-snippet="s_dynamic_snippet_products"
|
|
class="oe_unmovable oe_unremovable s_dynamic_snippet_products o_wsale_alternative_products s_dynamic pt32 pb32 o_colored_level s_product_product_borderless_1"
|
|
data-name="Alternative Products" style="background-image: none;" t-att-data-filter-id="product._get_alternative_product_filter()"
|
|
data-template-key="website_sale.dynamic_filter_template_product_product_products_item" data-product-category-id="all" data-number-of-elements="4"
|
|
data-number-of-elements-small-devices="1" data-number-of-records="16" data-carousel-interval="5000" data-bs-original-title="" title="">
|
|
<div class="container">
|
|
<div class="row s_nb_column_fixed">
|
|
<section class="s_dynamic_snippet_title oe_unremovable oe_unmovable d-flex flex-column flex-md-row justify-content-between mb-lg-0 pb-3 pb-md-0">
|
|
<div>
|
|
<h4>Alternative Products</h4>
|
|
<p class="lead">These other products might interest you</p>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<div class="o_not_editable">
|
|
<div class="dynamic_snippet_template"/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template
|
|
name="Terms and Conditions"
|
|
id="website_sale.product_terms_and_conditions"
|
|
active="True"
|
|
priority="21">
|
|
<small class="text-muted mb-0">
|
|
<a href="/terms" class="o_translate_inline text-muted"><u>Terms and Conditions</u></a><br/>
|
|
30-day money-back guarantee<br/>
|
|
Shipping: 2-3 Business Days
|
|
</small>
|
|
</template>
|
|
|
|
<!-- Product options: Zoom -->
|
|
<template inherit_id='website_sale.product' id="product_picture_magnify_click" name="Image Zoom On Click">
|
|
<xpath expr='//div[hasclass("o_wsale_product_page")]' position='attributes'>
|
|
<attribute name="data-ecom-zoom-click">1</attribute>
|
|
<attribute name="class" separator=" " add="ecom-zoomable" />
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Product options: OpenChatter -->
|
|
<template id="product_comment" inherit_id="website_sale.product" active="False" name="Discussion and Rating" priority="15">
|
|
<xpath expr="//div[@t-field='product.website_description']" position="after">
|
|
<div class="o_shop_discussion_rating border-top" data-anchor='true'>
|
|
<section id="o_product_page_reviews" class="container pt48 pb48" data-anchor='true'>
|
|
<a class="o_product_page_reviews_title d-flex justify-content-between text-reset text-decoration-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#o_product_page_reviews_content" aria-expanded="false" aria-controls="o_product_page_reviews_content">
|
|
<h2 class="fs-4 mb-0">Customer Reviews</h2>
|
|
<i class="oi align-self-center"/>
|
|
</a>
|
|
<div id="o_product_page_reviews_content" class="collapse mt-3">
|
|
<t t-call="portal.message_thread">
|
|
<t t-set="object" t-value="product"/>
|
|
<t t-set="display_rating" t-value="True"/>
|
|
<t t-set="message_per_page" t-value="5"/>
|
|
<t t-set="two_columns" t-value="true"/>
|
|
</t>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.product_quantity"
|
|
inherit_id="website_sale.cta_wrapper"
|
|
name="Select Quantity"
|
|
>
|
|
<div id="add_to_cart_wrap" position="inside">
|
|
<div
|
|
t-attf-class="css_quantity input-group {{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-flex order-first'}} align-middle border"
|
|
contenteditable="false"
|
|
>
|
|
<a
|
|
t-attf-href="#"
|
|
class="css_quantity_minus btn btn-link pe-2 js_add_cart_json border-0"
|
|
aria-label="Remove one"
|
|
title="Remove one"
|
|
name="remove_one"
|
|
>
|
|
<i class="oi oi-minus text-600"/>
|
|
</a>
|
|
<input
|
|
type="text"
|
|
t-attf-class="form-control quantity text-center border-0"
|
|
data-min="1"
|
|
name="add_qty"
|
|
t-att-value="1"
|
|
/>
|
|
<a
|
|
t-attf-href="#"
|
|
class="css_quantity_plus btn btn-link ps-2 js_add_cart_json border-0"
|
|
aria-label="Add one"
|
|
title="Add one"
|
|
>
|
|
<i class="oi oi-plus text-600"/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.product_quantity_large"
|
|
inherit_id="website_sale.product_quantity"
|
|
active="False"
|
|
name="Select Quantity Large"
|
|
>
|
|
<xpath expr="//div[contains(@t-attf-class, 'css_quantity')]" position="attributes">
|
|
<attribute
|
|
name="t-attf-class"
|
|
add="input-group-lg {{(' css_quantity_50' if is_50_pc else '')}}"
|
|
separator=" "
|
|
/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.product_buy_now"
|
|
inherit_id="website_sale.cta_wrapper"
|
|
active="False"
|
|
name="Buy Now Button"
|
|
>
|
|
<xpath expr="//div[@id='add_to_cart_wrap']" position="inside">
|
|
<a
|
|
role="button"
|
|
t-attf-class="btn btn-outline-primary o_we_buy_now w-100 {{'' if is_view_active('website_sale.cta_wrapper_boxed') or is_view_active('website_sale.cta_wrapper_large') or not is_50_pc else 'w-sm-auto'}} {{'flex-grow-1' if not is_50_pc else ''}}"
|
|
href="#"
|
|
>
|
|
<i class="fa fa-bolt me-2"/>
|
|
Buy now
|
|
</a>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template
|
|
id="website_sale.product_buy_now_large"
|
|
inherit_id="website_sale.product_buy_now"
|
|
active="False"
|
|
name="Large Buy Now Button"
|
|
>
|
|
<xpath expr="//a[contains(@t-attf-class, 'o_we_buy_now')]" position="attributes">
|
|
<attribute name="t-attf-class" add="btn-lg" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="website_sale.tax_indication" active="False">
|
|
<span
|
|
t-if="website.show_line_subtotals_tax_selection == 'tax_excluded'"
|
|
class="fs-6 fw-normal text-muted text-nowrap"
|
|
>
|
|
(Tax excluded)
|
|
</span>
|
|
<span t-else="" class="fs-6 fw-normal text-muted text-nowrap">
|
|
(Tax included)
|
|
</span>
|
|
</template>
|
|
|
|
<template id="product_price">
|
|
<div
|
|
name="product_price"
|
|
t-attf-class="product_price {{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-block'}}"
|
|
>
|
|
<div
|
|
name="product_price_container"
|
|
class="css_editable_mode_hidden d-flex align-items-baseline justify-content-end gap-2 flex-wrap h5 mb-0 text-wrap"
|
|
>
|
|
<span class="oe_price"
|
|
style="white-space: nowrap;"
|
|
t-out="combination_info['price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
<span t-attf-class="text-muted oe_default_price mb-0 h6 {{'' if combination_info['has_discounted_price'] else 'd-none'}}"
|
|
style="text-decoration: line-through; white-space: nowrap;"
|
|
t-out="combination_info['list_price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
name="product_list_price"
|
|
/>
|
|
<t t-if="is_view_active('website_sale.tax_indication')" t-call="website_sale.tax_indication"/>
|
|
<del
|
|
name="product_price_strikethrough"
|
|
t-if="combination_info.get('compare_list_price') and (combination_info['compare_list_price'] > combination_info['price'])"
|
|
class="text-muted ms-1 h6 oe_compare_list_price"
|
|
>
|
|
<bdi dir="inherit">
|
|
<span t-out="combination_info['compare_list_price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
</bdi>
|
|
</del>
|
|
</div>
|
|
<div
|
|
t-if="editable"
|
|
name="product_list_price_container"
|
|
class="css_non_editable_mode_hidden decimal_precision h5"
|
|
t-att-data-precision="str(website.currency_id.decimal_places)"
|
|
>
|
|
<span t-field="product.list_price"
|
|
t-options="{'widget': 'monetary', 'display_currency': product.currency_id}"/>
|
|
<t t-if="is_view_active('website_sale.tax_indication')" t-call="website_sale.tax_indication"/>
|
|
<del t-if="combination_info.get('compare_list_price') and (combination_info['compare_list_price'] > combination_info['price'])">
|
|
<bdi dir="inherit">
|
|
<span t-field="product.compare_list_price"
|
|
t-options="{'widget': 'monetary', 'display_currency': product.currency_id}"/>
|
|
</bdi>
|
|
</del>
|
|
</div>
|
|
<small t-if="combination_info.get('tax_disclaimer')" class="text-muted">
|
|
<t t-out="combination_info['tax_disclaimer']"/>
|
|
</small>
|
|
</div>
|
|
<div id="product_unavailable" t-attf-class="{{'d-flex' if combination_info['prevent_zero_price_sale'] else 'd-none'}}">
|
|
<br/>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="wizard_checkout" name="Wizard Checkout">
|
|
<t t-call="website.step_wizard">
|
|
<t t-set="wizard_step" t-value="website._get_checkout_steps()"/>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- /shop/extra_info route -->
|
|
<template id="extra_info" name="Checkout Extra Info" active="False">
|
|
<t t-call="website_sale.checkout_layout">
|
|
<t t-set="redirect" t-valuef="/shop/extra_info"/>
|
|
<t t-set="oe_structure">
|
|
<!-- This is the drag-and-drop area for website building blocs at the end of each
|
|
checkout page. This is append at the of the page in `checkout_layout`. The
|
|
templates created in the database to store blocs are hooked using XPath on the
|
|
`oe_struture` element ID. Therefore, we can't use dynamic IDs (like with
|
|
t-att-id) and each template needs to define a div element. -->
|
|
<div class="oe_structure" id="oe_structure_website_sale_extra_info_1"/>
|
|
</t>
|
|
|
|
<h4 class="mb-3">Extra info</h4>
|
|
<section class="s_website_form" data-vcss="001" data-snippet="s_website_form">
|
|
<div class="container">
|
|
<form
|
|
action="/website/form/"
|
|
method="post"
|
|
enctype="multipart/form-data"
|
|
class="o_mark_required s_website_form_no_recaptcha"
|
|
data-mark="*"
|
|
data-force_action="shop.sale.order"
|
|
data-model_name="sale.order"
|
|
data-success-mode="redirect"
|
|
data-success-page="/shop/payment"
|
|
hide-change-model="true"
|
|
>
|
|
<div class="s_website_form_rows s_col_no_bgcolor row">
|
|
<span id="s_website_form_result"/>
|
|
<div class="s_website_form_field col-12 pb-2 mb-0" data-type="char" data-name="Field">
|
|
<div class="s_col_no_resize s_col_no_bgcolor">
|
|
<label class="s_website_form_label col-form-label col-sm-auto" style="width: 200px" for="sale1">
|
|
<span class="s_website_form_label_content">Your Reference</span>
|
|
</label>
|
|
<div class="col-sm">
|
|
<input id="sale1" type="text" class="s_website_form_input form-control" name="client_order_ref"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="s_website_form_field s_website_form_custom col-12 pb-2 mb-0" data-type="text" data-name="Field">
|
|
<div class="s_col_no_resize s_col_no_bgcolor">
|
|
<label class="s_website_form_label col-form-label col-sm-auto" style="width: 200px" for="sale2">
|
|
<span class="s_website_form_label_content">Give us your feedback</span>
|
|
</label>
|
|
<div class="col-sm">
|
|
<textarea id="sale2" class="s_website_form_input form-control" name="Give us your feedback" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="s_website_form_field s_website_form_custom col-12 pb-2 mb-0" data-type="binary" data-name="Field">
|
|
<div class="s_col_no_resize s_col_no_bgcolor">
|
|
<label class="s_website_form_label col-form-label col-sm-auto" style="width: 200px" for="sale3">
|
|
<span class="s_website_form_label_content">Upload a document</span>
|
|
</label>
|
|
<div class="col-sm">
|
|
<input id="sale3" type="file" class="s_website_form_input form-control" name="a_document" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Encapsulate the content in a `a` tag with a link to the product page. Override this
|
|
template to change or remove the product link. Called in `website_sale.cart_lines`. -->
|
|
<template id="cart_line_product_link" name="Shopping Cart Line Product Link">
|
|
<a
|
|
t-if="line._is_sellable()"
|
|
class="text-reset"
|
|
t-att-href="line.product_id.website_url"
|
|
name="o_cart_line_product_link"
|
|
>
|
|
<t t-out="0"/>
|
|
</a>
|
|
<t t-else="" t-out="0"/>
|
|
</template>
|
|
|
|
<!-- This template displays all the lines following the first one on the description of the sale
|
|
order line, with a muted style. For typical products this content will be the product
|
|
description_sale. Called in `website_sale.cart_lines`. -->
|
|
<template id="cart_line_description_following_lines" name="Shopping Cart Line Description Following Lines">
|
|
<t t-set="description_lines" t-value="line.get_description_following_lines()"/>
|
|
<div t-if="description_lines" class="text-muted small">
|
|
<t t-foreach="description_lines" t-as="name_line">
|
|
<span
|
|
t-if="name_line"
|
|
t-attf-class="d-block #{not name_line_last and 'mb-1'}"
|
|
t-out="name_line"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Lines that show items in the cart. Called in `website_sale.cart`. -->
|
|
<template id="cart_lines" name="Shopping Cart Lines">
|
|
<div
|
|
t-if="not website_sale_order or not website_sale_order.website_order_line"
|
|
class="js_cart_lines w-100"
|
|
>
|
|
<div class="mt-5 mb-3"><t t-call="website_sale.empty_cart_svg"/></div>
|
|
<h5 class="mb-3" style="text-align: center;">
|
|
Your cart is empty!
|
|
</h5>
|
|
<p style="text-align: center;">
|
|
<a href="/shop" class="btn btn-primary">
|
|
Shop
|
|
</a>
|
|
</p>
|
|
</div>
|
|
<t t-if='website_sale_order'>
|
|
<div t-if='website_sale_order._get_shop_warning(clear=False)' class="alert alert-warning js_cart_lines" role="alert">
|
|
<strong>Warning!</strong>
|
|
<div t-out="website_sale_order._get_shop_warning()" class="text-prewrap"/>
|
|
</div>
|
|
</t>
|
|
<div id="cart_products"
|
|
t-if="website_sale_order and website_sale_order.website_order_line"
|
|
class="js_cart_lines d-flex flex-column mb32">
|
|
<t t-foreach="website_sale_order.website_order_line" t-as="line">
|
|
<t t-set="is_combo" t-value="line.product_type == 'combo'"/>
|
|
<div
|
|
class="o_cart_product d-flex gap-3 pb-4"
|
|
t-attf-data-product-id="#{line.product_id and line.product_id.id}"
|
|
>
|
|
<div
|
|
class="o_cart_product_image"
|
|
>
|
|
<!--
|
|
Unsellable lines can have unpublished products, but portal users have no
|
|
access to unpublished product images. To ensure product images are
|
|
always shown for unsellable lines, we use the raw image data as src
|
|
(which doesn't require access, unlike the image URL).
|
|
-->
|
|
<t t-call="website_sale.cart_line_product_link">
|
|
<img
|
|
t-if="not line._is_sellable() and line.product_id.image_128"
|
|
t-att-src="image_data_uri(line.product_id.image_128)"
|
|
class="img border rounded"
|
|
t-att-alt="line.name_short"
|
|
/>
|
|
<div
|
|
t-else=""
|
|
t-field="line.product_id.image_128"
|
|
t-options="{
|
|
'widget': 'image',
|
|
'qweb_img_responsive': False,
|
|
'class': 'border rounded',
|
|
}"
|
|
/>
|
|
</t>
|
|
</div>
|
|
<div class="d-flex flex-column flex-grow-1 gap-3 min-w-0">
|
|
<div class="d-flex gap-3">
|
|
<div class="flex-grow-1 text-wrap w-100">
|
|
<div class="d-flex justify-content-between">
|
|
<div class="d-md-flex flex-md-wrap column-gap-md-1 align-items-md-center">
|
|
<t t-call="website_sale.cart_line_product_link">
|
|
<h6
|
|
t-out="line._get_line_header()"
|
|
class="text-wrap mb-1"
|
|
/>
|
|
</t>
|
|
<t t-set="combination_name" t-value="line._get_combination_name()"/>
|
|
<span t-if="combination_name" class="d-none d-md-inline h6 text-muted mb-1">-</span>
|
|
<div
|
|
t-if="combination_name or line.product_template_id._has_multiple_uoms()"
|
|
class="d-inline-flex flex-wrap column-gap-2 row-gap-1 align-items-center my-1 my-md-0 w-100 w-md-auto"
|
|
>
|
|
<span
|
|
t-if="combination_name"
|
|
class="h6 text-muted mb-1"
|
|
t-out="combination_name"
|
|
/>
|
|
<span
|
|
t-if="line.product_template_id._has_multiple_uoms()"
|
|
class="badge bg-light mb-1"
|
|
t-out="line.product_uom_id.name"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div t-attf-class="{{'d-block d-md-none' if not is_combo else ''}}"><t t-call="website_sale.cart_lines_price"/></div>
|
|
</div>
|
|
<t t-call="website_sale.cart_line_description_following_lines"/>
|
|
<ul t-if="is_combo" class="list-group">
|
|
<t
|
|
t-foreach="line.linked_line_ids.filtered('combo_item_id')"
|
|
t-as="combo_item_line"
|
|
>
|
|
<t t-call="website_sale.cart_combo_item_line"/>
|
|
</t>
|
|
</ul>
|
|
<div
|
|
name="o_wsale_cart_line_button_container"
|
|
t-attf-class="d-none d-md-flex align-items-center gap-2 mt-2 {{'justify-content-between' if is_combo else ''}}"
|
|
>
|
|
<a
|
|
href='#'
|
|
class="js_delete_product small"
|
|
aria-label="Remove from cart"
|
|
title="Remove from cart"
|
|
>
|
|
Remove
|
|
</a>
|
|
<div t-if="is_combo" class="d-none d-md-block ms-auto"><t t-call="website_sale.cart_lines_quantity"/></div>
|
|
</div>
|
|
</div>
|
|
<div t-if="not is_combo" class="d-none d-md-block">
|
|
<t t-call="website_sale.cart_lines_price"/>
|
|
<div class="d-none d-md-block mt-2"><t t-call="website_sale.cart_lines_quantity"/></div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
name="o_wsale_cart_line_button_container_mobile"
|
|
class="d-flex d-md-none"
|
|
>
|
|
<div class="d-flex d-md-none align-items-center me-auto">
|
|
<t t-call="website_sale.cart_lines_quantity" is_mobile="True"/>
|
|
</div>
|
|
<button
|
|
class="js_delete_product btn btn-link d-inline-block d-md-none px-2"
|
|
title="Remove"
|
|
>
|
|
<i class="fa fa-fw fa-trash-o"/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="cart_lines_quantity" name="Shopping Cart Line quantity">
|
|
<t
|
|
t-set="should_show_quantity_selector"
|
|
t-value="is_view_active('website_sale.product_quantity')"
|
|
/>
|
|
<div
|
|
t-attf-class="css_quantity input-group justify-content-end h-100 {{should_show_quantity_selector and line._is_sellable() and 'border' or ''}}"
|
|
t-attf-name="{{'website_sale_cart_line_quantity' if not is_mobile else 'website_sale_cart_line_quantity_mobile'}}"
|
|
>
|
|
<t t-if="should_show_quantity_selector and line._is_sellable()">
|
|
<a
|
|
href="#"
|
|
class="btn btn-link d-inline-block border-end-0"
|
|
aria-label="Remove one"
|
|
title="Remove one"
|
|
>
|
|
<i class="oi oi-minus position-relative z-1"/>
|
|
</a>
|
|
<input
|
|
type="text"
|
|
class="js_quantity quantity form-control border-0"
|
|
t-att-data-line-id="line.id"
|
|
t-att-data-product-id="line.product_id.id"
|
|
t-att-value="line._get_displayed_quantity()"
|
|
/>
|
|
<t t-if="line._get_shop_warning(clear=False)">
|
|
<a href="#" class="btn btn-link">
|
|
<i
|
|
class="fa fa-warning text-warning"
|
|
t-att-title="line._get_shop_warning()"
|
|
role="img"
|
|
aria-label="Warning"
|
|
/>
|
|
</a>
|
|
</t>
|
|
<a
|
|
t-else=""
|
|
href="#"
|
|
class="btn btn-link d-inline-block border-start-0"
|
|
aria-label="Add one"
|
|
title="Add one"
|
|
>
|
|
<i class="oi oi-plus position-relative z-1"/>
|
|
</a>
|
|
</t>
|
|
<t t-else="">
|
|
<input
|
|
type="text"
|
|
class="js_quantity form-control quantity text-start text-md-end text-md-end border-0 p-0 shadow-none mw-100"
|
|
t-att-data-line-id="line.id"
|
|
t-att-data-product-id="line.product_id.id"
|
|
t-att-value="line._get_displayed_quantity()"
|
|
readonly="True"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="cart_lines_price" name="Shopping Cart Line Price">
|
|
<h6
|
|
class="d-flex flex-column flex-md-row align-items-end align-items-md-start justify-content-md-end mb-1"
|
|
name="website_sale_cart_line_price"
|
|
>
|
|
<del
|
|
t-if="line._should_show_strikethrough_price()"
|
|
class="me-md-2 text-muted text-nowrap opacity-50"
|
|
t-out="line._get_displayed_unit_price() * line.product_uom_qty"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
/>
|
|
<t t-set="product_price" t-value="line._get_cart_display_price()"/>
|
|
<t t-call="website_sale.cart_product_price"/>
|
|
</h6>
|
|
<small
|
|
t-if="line._is_sellable() and line.env['res.groups']._is_feature_enabled('website_sale.group_show_uom_price') and line.product_id.base_unit_price"
|
|
class="cart_product_base_unit_price d-block text-muted text-end"
|
|
groups="website_sale.group_show_uom_price"
|
|
>
|
|
<t t-call="website_sale.base_unit_price">
|
|
<t t-set="product" t-value="line.product_id"/>
|
|
<t
|
|
t-set="base_unit_price"
|
|
t-value="product._get_base_unit_price(product_price/line.product_qty)"
|
|
/>
|
|
</t>
|
|
</small>
|
|
</template>
|
|
|
|
<template id="cart_combo_item_line">
|
|
<a
|
|
t-att-href="combo_item_line.product_id.website_url if combo_item_line._is_sellable() and combo_item_line.product_id.website_published else '#'"
|
|
t-attf-class="list-group-item #{'list-group-item-action' if combo_item_line._is_sellable() and combo_item_line.product_id.website_published else 'cursor-default text-decoration-none'}"
|
|
>
|
|
<h6 class="d-inline small">
|
|
<span t-out="combo_item_line._get_displayed_quantity()"/>
|
|
x
|
|
<span t-out="combo_item_line.name_short"/>
|
|
</h6>
|
|
<t
|
|
t-set="description_lines"
|
|
t-value="combo_item_line.get_description_following_lines()"
|
|
/>
|
|
<div t-if="description_lines" class="small text-muted">
|
|
<t t-foreach="description_lines" t-as="description_line">
|
|
<span
|
|
t-if="description_line"
|
|
class="d-block"
|
|
t-out="description_line"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</a>
|
|
</template>
|
|
|
|
<template id="cart_product_price">
|
|
<span
|
|
t-out="product_price"
|
|
style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"
|
|
/>
|
|
</template>
|
|
|
|
<!-- /shop/cart route -->
|
|
<template id="cart" name="Shopping Cart">
|
|
<t t-call="website_sale.checkout_layout">
|
|
<t t-set="show_shorter_cart_summary" t-value="True"/>
|
|
<t t-set="show_footer" t-value="True"/>
|
|
<t t-set="oe_structure">
|
|
<!-- This is the drag-and-drop area for website building blocs at the end of each
|
|
checkout page. This is append at the of the page in `checkout_layout`. The
|
|
templates created in the database to store blocs are hooked using XPath on the
|
|
`oe_struture` element ID. Therefore, we can't use dynamic IDs (like with
|
|
t-att-id) and each template needs to define a div element. -->
|
|
<div class="oe_structure" id="oe_structure_website_sale_cart_2"/>
|
|
</t>
|
|
<div id="shop_cart" class="col">
|
|
<div class="d-flex align-items-center mb-3">
|
|
<h4 class="mb-0">Order summary</h4>
|
|
<div class="ms-3 border-start ps-2"><t t-call="website_sale.quick_reorder_button"/></div>
|
|
</div>
|
|
<div t-if="abandoned_proceed or access_token" class="alert alert-info mt8 mb8" role="alert"> <!-- abandoned cart choices -->
|
|
<t t-if="abandoned_proceed">
|
|
<p>Your previous cart has already been completed.</p>
|
|
<p t-if="website_sale_order">Please proceed your current cart.</p>
|
|
</t>
|
|
<t t-if="access_token">
|
|
<p>This is your current cart.</p>
|
|
<p>
|
|
<strong>
|
|
<a class="o_translate_inline" t-attf-href="/shop/cart/?id={{id}}&access_token={{access_token}}&revive_method=squash">Click here</a>
|
|
</strong> if you want to restore your previous cart. Your current cart will be replaced with your previous cart.
|
|
</p>
|
|
<p>
|
|
<strong>
|
|
<a class="o_translate_inline" t-attf-href="/shop/cart/?id={{id}}&access_token={{access_token}}&revive_method=merge">Click here</a>
|
|
</strong> if you want to merge your previous cart into current cart.
|
|
</p>
|
|
</t>
|
|
</div>
|
|
<t t-call="website_sale.cart_lines"/>
|
|
<div class="clearfix" />
|
|
<div class="oe_structure" id="oe_structure_website_sale_cart_1"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="quick_reorder_button">
|
|
<t t-set="quick_reorder_button_title">
|
|
<t t-if="request.website.is_public_user()">Login to reorder</t>
|
|
<t t-elif="not order_history">No previous products available for reorder.</t>
|
|
</t>
|
|
<span t-att-title="quick_reorder_button_title">
|
|
<button
|
|
id="quick_reorder_button"
|
|
type="button"
|
|
class="btn btn-sm btn-link"
|
|
data-bs-toggle="offcanvas"
|
|
data-bs-target="#quick_reorder_sidebar"
|
|
aria-controls="quick_reorder_sidebar"
|
|
t-att-disabled="not order_history"
|
|
>
|
|
<i class="fa fa-rotate-left me-2"/>Quick reorder
|
|
</button>
|
|
</span>
|
|
<t t-call="website_sale.quick_reorder_sidebar"/>
|
|
</template>
|
|
|
|
<template id="quick_reorder_sidebar">
|
|
<div
|
|
id="quick_reorder_sidebar"
|
|
class="offcanvas offcanvas-end"
|
|
aria-labelledby="quick_reorder_sidebar"
|
|
>
|
|
<div class="offcanvas-header">
|
|
<h5 class="offcanvas-title">Quick reorder</h5>
|
|
<button
|
|
type="button"
|
|
class="btn-close text-reset"
|
|
data-bs-dismiss="offcanvas"
|
|
aria-label="Close"
|
|
/>
|
|
</div>
|
|
<div class="offcanvas-body">
|
|
<!-- Parent element needed for proper JS side rendering -->
|
|
<t t-if="order_history" t-call="website_sale.quick_reorder_history"/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="quick_reorder_history">
|
|
<div
|
|
class="o_wsale_quick_reorder_line_group ps-sm-3"
|
|
t-foreach="order_history"
|
|
t-as="order_line_group"
|
|
>
|
|
<div class="o_wsale_quick_reorder_group_content d-flex gap-3 w-100 position-relative">
|
|
<div
|
|
class="o_dot_line d-none d-sm-block position-absolute top-0 bottom-0 mb-1 border-start"
|
|
/>
|
|
<span
|
|
class="o_dot d-none d-sm-block position-absolute translate-middle-x text-o-color-1"
|
|
/>
|
|
<div class="w-100 ps-sm-4">
|
|
<h6 t-out="order_line_group['label']" class="mt-1 mb-3 text-muted"/>
|
|
<div
|
|
t-foreach="order_line_group['lines']"
|
|
t-as="order_line"
|
|
t-att-class="'o_wsale_quick_reorder_line d-flex flex-column flex-sm-row justify-content-between gap-3 '
|
|
+ ('align-items-start' if order_line.product_type == 'combo' else '')"
|
|
>
|
|
<div
|
|
t-att-class="'d-flex gap-2 ' + ('align-items-start' if order_line.product_type == 'combo' else 'align-items-center')"
|
|
>
|
|
<div class="o_cart_product_image">
|
|
<div
|
|
t-field="order_line.product_id.image_128"
|
|
t-options="{
|
|
'widget': 'image',
|
|
'qweb_img_responsive': False,
|
|
'class': 'border rounded',
|
|
}"
|
|
/>
|
|
</div>
|
|
<div
|
|
class="d-flex flex-column align-items-start justify-content-center flex-grow-1 h-100"
|
|
>
|
|
<div>
|
|
<h6
|
|
t-out="order_line.name_short"
|
|
t-att-class="'small ' + ('mb-2' if order_line.product_type == 'combo' else 'mb-0')"
|
|
/>
|
|
<ul
|
|
t-if="order_line.product_type == 'combo'" class="list-group"
|
|
>
|
|
<t
|
|
t-foreach="order_line.linked_line_ids"
|
|
t-as="combo_item_line"
|
|
t-call="website_sale.cart_combo_item_line"
|
|
/>
|
|
</ul>
|
|
</div>
|
|
<small
|
|
t-if="order_line.product_template_id._has_multiple_uoms()"
|
|
t-out="order_line.product_uom_id.name"
|
|
class="badge mt-1 bg-light"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center ms-auto gap-2">
|
|
<t
|
|
t-set="combination_info"
|
|
t-value="order_line.product_id._get_combination_info_variant()"
|
|
/>
|
|
<small
|
|
t-out="combination_info['price'] * order_line.product_uom_qty"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': website.currency_id,
|
|
}"
|
|
class="o_wsale_quick_reorder_product_price"
|
|
style="white-space: nowrap;"
|
|
/>
|
|
<input
|
|
type="text"
|
|
t-att-value="int(order_line.product_uom_qty)"
|
|
class="o_wsale_quick_reorder_qty_input form-control text-center"
|
|
t-att-data-price-unit="combination_info['price']"
|
|
t-att-data-currency-digits="combination_info['currency'].decimal_places"
|
|
/>
|
|
<button
|
|
type="button"
|
|
title="Press 'Enter' to add to cart"
|
|
class="o_wsale_quick_reorder_product_button btn btn-outline-primary"
|
|
t-att-data-quantity="order_line.product_uom_qty"
|
|
t-att-data-product-id="order_line.product_id.id"
|
|
t-att-data-product-template-id="order_line.product_id.product_tmpl_id.id"
|
|
t-att-data-product-type="order_line.product_id.type"
|
|
t-att-data-selected-combo-items="json.dumps(order_line._get_selected_combo_items())"
|
|
>
|
|
<i class="fa fa-cart-plus"/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Deactivatable through the website editor. -->
|
|
<template id="suggested_products_list" inherit_id="website_sale.cart_lines" name="Accessory Products in my cart">
|
|
<xpath expr="//div[@id='cart_products']" position="inside">
|
|
<h5 t-attf-class="mt-3 mb-0" t-if="suggested_products">Suggested accessories</h5>
|
|
<div t-if="suggested_products"
|
|
id="suggested_products"
|
|
class="d-flex flex-column align-items-stretch">
|
|
<div
|
|
t-foreach="suggested_products"
|
|
t-as="product"
|
|
t-attf-class="d-flex gap-3 py-4 #{not product_last and 'border-bottom'}"
|
|
t-att-data-publish="product.website_published and 'on' or 'off'"
|
|
name="suggested_product"
|
|
>
|
|
<div class="o_cart_product_image">
|
|
<a t-att-href="product.website_url">
|
|
<div t-field="product.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'border rounded'}"/>
|
|
</a>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<a class="text-reset" t-att-href="product.website_url">
|
|
<h6
|
|
class="align-top text-wrap"
|
|
t-out="product.with_context(display_default_code=False).display_name"
|
|
/>
|
|
</a>
|
|
<div
|
|
class="mb-2 small text-muted"
|
|
t-field="product.description_sale"
|
|
/>
|
|
</div>
|
|
<div class="d-flex flex-column gap-2 align-items-end">
|
|
<h6
|
|
class="d-flex mb-0 text-end"
|
|
name="suggested_product_price_container"
|
|
>
|
|
<t
|
|
t-set="combination_info"
|
|
t-value="product._get_combination_info_variant()"
|
|
/>
|
|
<del
|
|
name="suggested_product_list_price"
|
|
t-attf-class="me-2 text-nowrap text-muted {{'' if combination_info['has_discounted_price'] else 'd-none'}}"
|
|
t-out="combination_info['list_price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
/>
|
|
<span
|
|
name="suggested_product_price"
|
|
class="text-nowrap"
|
|
t-out="combination_info['price']"
|
|
t-options="{'widget': 'monetary','display_currency': website.currency_id}"
|
|
/>
|
|
</h6>
|
|
<button
|
|
class="js_add_suggested_products btn btn-primary text-nowrap"
|
|
t-att-data-product-id="product.id"
|
|
t-att-data-product-template-id="product.product_tmpl_id.id"
|
|
t-att-data-product-type="product.type"
|
|
t-att-data-show-quantity="is_view_active('website_sale.product_quantity')"
|
|
>
|
|
<i class="d-md-none fa fa-shopping-cart" role="presentation"/>
|
|
<span class="d-none d-md-inline">Add to cart</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.reduction_code`. -->
|
|
<template id='coupon_form' name='Coupon form'>
|
|
<!-- Checkout context:
|
|
- redirect: The route to redirect to when a customer enters a coupon; default: `None`.
|
|
- website_sale_order: The current order.
|
|
-->
|
|
<form
|
|
class="mb-3"
|
|
t-attf-action="/shop/pricelist#{redirect and '?r=' + redirect or ''}"
|
|
method="post"
|
|
name="coupon_code"
|
|
>
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<div class="input-group w-100">
|
|
<input name="promo" class="form-control" type="text" placeholder="Discount code..." t-att-value="website_sale_order.pricelist_id.code or None"/>
|
|
<button type="submit" class="btn btn-light border">Apply</button>
|
|
</div>
|
|
</form>
|
|
<t t-if="request.params.get('code_not_available')" name="code_not_available">
|
|
<div class="alert alert-danger text-start" role="alert">This promo code is not available.</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.checkout_layout`. -->
|
|
<template id="navigation_buttons" name="Navigation buttons">
|
|
<!-- Layout customization parameters:
|
|
- hide_payment_button: Whether the payment button should be hidden; default: False.
|
|
-->
|
|
<!-- Checkout context:
|
|
- website_sale_order: The current order.
|
|
-->
|
|
<div t-attf-class="#{_container_classes} d-flex flex-column gap-2">
|
|
<t t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<t
|
|
t-if="(website.account_on_checkout != 'mandatory' or
|
|
not website.is_public_user()) and show_shorter_cart_summary"
|
|
t-call="payment.express_checkout"
|
|
/>
|
|
<t t-if="current_website_checkout_step_href == '/shop/payment'">
|
|
<div t-if="not errors and not website_sale_order.amount_total"
|
|
name="o_website_sale_free_cart">
|
|
<form name="o_wsale_confirm_order"
|
|
class="d-flex flex-column"
|
|
target="_self"
|
|
action="/shop/payment/validate"
|
|
method="post">
|
|
<input type="hidden"
|
|
name="csrf_token"
|
|
t-att-value="request.csrf_token()"/>
|
|
<t t-if="not hide_payment_button" t-call="payment.submit_button">
|
|
<t t-set="submit_button_label">Confirm Order</t>
|
|
</t>
|
|
</form>
|
|
</div>
|
|
<t t-elif="not hide_payment_button" t-call="payment.submit_button"/>
|
|
</t>
|
|
<t t-else="">
|
|
<a role="button" name="website_sale_main_button"
|
|
t-attf-class="btn btn-primary #{not website_sale_order._is_cart_ready() and 'disabled'} w-100"
|
|
t-att-href="next_website_checkout_step_href">
|
|
<span t-field="next_website_checkout_step.main_button_label"/>
|
|
<i class="fa fa-angle-right ms-2 fw-light"/>
|
|
</a>
|
|
</t>
|
|
</t>
|
|
<div t-if="not hide_payment_button" class="position-relative d-none d-lg-flex w-100 justify-content-center align-items-center my-2 opacity-75">
|
|
<hr class="w-100"/>
|
|
<span class="px-3">or</span>
|
|
<hr class="w-100"/>
|
|
</div>
|
|
<t t-if="previous_website_checkout_step">
|
|
<a
|
|
t-att-href="previous_website_checkout_step.step_href"
|
|
class="mt-2 mt-lg-0 text-center"
|
|
>
|
|
<i class="fa fa-angle-left me-2 fw-light"/>
|
|
<span t-field="previous_website_checkout_step.back_button_label"/>
|
|
</a>
|
|
</t>
|
|
<t t-else="">
|
|
<a t-att-href="'/shop'" class="mt-2 mt-lg-0 text-center">
|
|
<i class="fa fa-angle-left me-2 fw-light"/>
|
|
Continue shopping
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- /shop/checkout route -->
|
|
<template id="checkout">
|
|
<t t-call="website_sale.checkout_layout">
|
|
<t t-set="additional_title">Shop - Checkout</t>
|
|
<t t-set="redirect" t-valuef="/shop/checkout"/>
|
|
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_invoice_id or only_services)" />
|
|
<div id="shop_checkout">
|
|
<t t-if="order._has_deliverable_products()">
|
|
<div class="mb-4">
|
|
<t t-call="website_sale.delivery_form">
|
|
<t t-set="selected_dm_id" t-value="order.carrier_id.id"/>
|
|
</t>
|
|
</div>
|
|
<div class="mb-4">
|
|
<t t-call="website_sale.delivery_address_list">
|
|
<t t-set="addresses" t-value="delivery_addresses"/>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
<t t-call="website_sale.billing_address_list">
|
|
<t t-set="addresses" t-value="billing_addresses"/>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="delivery_address_list">
|
|
<t t-set="address_type" t-value="'delivery'"/>
|
|
<t
|
|
t-set="new_address_url"
|
|
t-valuef="{{address_url}}?address_type={{address_type}}&use_delivery_as_billing={{use_delivery_as_billing}}"
|
|
/>
|
|
<div id="delivery_address_list">
|
|
<div class="d-flex justify-content-between align-items-start gap-3 mb-3">
|
|
<h5 name="delivery_address_title" class="mb-0">Delivery address</h5>
|
|
<!-- We don't allow public users to have multiple delivery addresses. -->
|
|
<a
|
|
t-if="not order.website_id.is_public_user()"
|
|
role="button"
|
|
t-att-href="new_address_url"
|
|
t-att-data-address-type="address_type"
|
|
class="o_address_card_add_new btn btn-outline-primary btn-sm"
|
|
title="Add an address"
|
|
aria-label="Add an address"
|
|
>
|
|
<i class="oi oi-plus me-2"/>Add Address
|
|
</a>
|
|
</div>
|
|
<t t-call="website_sale.address_list">
|
|
<t t-set="addresses" t-value="delivery_addresses"/>
|
|
<t t-set="selected_address" t-value="order.partner_shipping_id"/>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="billing_address_list">
|
|
<t t-set="address_type" t-value="'billing'"/>
|
|
<t t-set="new_address_url" t-valuef="{{address_url}}?address_type={{address_type}}"/>
|
|
<t t-set="has_delivery" t-value="order._has_deliverable_products()"/>
|
|
<div id="billing_address_list" class="mb-3 pt-2">
|
|
<div class="d-flex justify-content-between align-items-start gap-3">
|
|
<p t-attf-class="mb-0 {{only_services and 'h4' or 'h5'}}">
|
|
Billing address
|
|
</p>
|
|
<a
|
|
role="button"
|
|
t-att-href="new_address_url"
|
|
t-att-data-address-type="address_type"
|
|
t-attf-class="o_address_card_add_new o_add_billing_address_btn btn btn-outline-primary btn-sm {{'d-none' if use_delivery_as_billing and has_delivery else ''}}"
|
|
title="Add an address"
|
|
aria-label="Add an address"
|
|
>
|
|
<i class="oi oi-plus me-2"/>Add Address
|
|
</a>
|
|
</div>
|
|
<div t-if="has_delivery" class="form-check form-switch mt-2">
|
|
<label id="use_delivery_as_billing_label">
|
|
<input
|
|
type="checkbox"
|
|
id="use_delivery_as_billing"
|
|
class="form-check-input"
|
|
t-att-checked="use_delivery_as_billing"
|
|
/>
|
|
<span name="use_delivery_as_billing_text">
|
|
Same as delivery address
|
|
</span>
|
|
</label>
|
|
</div>
|
|
<div
|
|
id="billing_container"
|
|
t-attf-class="mt-3 {{'d-none' if use_delivery_as_billing and has_delivery else ''}}"
|
|
>
|
|
<t t-call="website_sale.address_list">
|
|
<t t-set="addresses" t-value="billing_addresses"/>
|
|
<t t-set="selected_address" t-value="order.partner_invoice_id"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.address_list" inherit_id="portal.address_list" primary="True">
|
|
<t t-call="portal.address_card" position="replace">
|
|
<t t-call="website_sale.address_card">
|
|
<!-- Drop show_removal and address_update_url which were in the base call to portal.address_card -->
|
|
<t t-set="is_user_address" t-value="address == request.env.user.partner_id"/>
|
|
<t t-set="contact" t-value="address"/>
|
|
<t
|
|
t-set="can_be_edited"
|
|
t-value="contact._can_be_edited_by_current_customer(order_sudo=order)"
|
|
/>
|
|
<t t-set="selected" t-value="address == selected_address"/>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- To ensure modification dones in the checkout are not applied on the portal -->
|
|
<template id="website_sale.address_card" inherit_id="portal.address_card" primary="True"/>
|
|
|
|
<!-- /shop/address route -->
|
|
<template id="website_sale.address_form_fields" inherit_id="portal.address_form_fields" primary="True">
|
|
<t name="b2b_fields" position="attributes">
|
|
<attribute name="t-if">display_b2b_fields</attribute>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- /shop/address route -->
|
|
<template id="website_sale.address" name="Address Management">
|
|
<t t-call="website_sale.checkout_layout">
|
|
<div class="o_customer_address_fill">
|
|
<div>
|
|
<t t-if="not is_anonymous_cart">
|
|
<h4 class="mb-3">
|
|
<t t-if="partner_sudo">Edit address</t>
|
|
<t t-else="">New address</t>
|
|
</h4>
|
|
</t>
|
|
<div
|
|
t-if="use_delivery_as_billing and not only_services and partner_sudo"
|
|
class="alert alert-warning"
|
|
role="alert"
|
|
>
|
|
<p class="mb-0">
|
|
You are editing your <b>delivery and billing</b> addresses
|
|
at the same time!<br/>
|
|
If you want to modify your billing address, create a
|
|
<a class="o_translate_inline" href="/shop/address?address_type=billing">new address</a>.
|
|
</p>
|
|
</div>
|
|
<div id="errors"/> <!-- for js -->
|
|
<form
|
|
action="/shop/address/submit"
|
|
method="post"
|
|
name="address_form"
|
|
class="address_autoformat"
|
|
t-att-data-company-country-code="res_company.country_id.code"
|
|
t-att-data-submit-url="'/shop/address/submit'"
|
|
>
|
|
<t t-if="is_anonymous_cart">
|
|
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between mb-1">
|
|
<h4 class="w-100 w-md-auto">Details</h4>
|
|
<div
|
|
t-if="website.account_on_checkout != 'disabled'"
|
|
class="o_address_signin d-flex d-md-block justify-content-between align-items-center p-3 p-md-0 w-100 text-md-end"
|
|
>
|
|
<span class="align-middle">Already have an account?</span>
|
|
<a
|
|
role="button"
|
|
href='/web/login?redirect=/shop/checkout'
|
|
class="btn btn-primary ms-2"
|
|
>
|
|
Sign in
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<div class="row">
|
|
<t t-call="website_sale.address_form_fields"/>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Deactivatable through the website editor. -->
|
|
<template id="address_b2b" inherit_id="website_sale.address" name="Show b2b fields" />
|
|
|
|
<template id="address_edit_button" name="Address edit button">
|
|
<t t-if="xmlid != 'website_sale.confirmation'">
|
|
<a t-if="order.partner_invoice_id.country_id" class="float-end no-decoration" href="/shop/checkout">
|
|
<i class="fa fa-pencil me-1"/>Edit
|
|
</a>
|
|
<a
|
|
t-else=""
|
|
class="float-end no-decoration"
|
|
t-attf-href="/shop/address?partner_id={{order.partner_invoice_id.id}}&address_type=billing"
|
|
>
|
|
<i class="fa fa-pencil me-1"/>Want an invoice?
|
|
</a>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.payment` and `website_sale.confirmation`. -->
|
|
<template id="address_on_checkout" name="Address on payment">
|
|
<div class="card">
|
|
<div class="card-body" id="delivery_and_billing">
|
|
<t t-call="website_sale.address_edit_button"/>
|
|
<t t-set="delivery_title_classes" t-value="'badge text-bg-info mb-2'"/>
|
|
<t
|
|
t-set="use_delivery_as_billing"
|
|
t-value="order.partner_invoice_id == order.partner_shipping_id and not order.pickup_location_data"
|
|
/>
|
|
<div class="d-flex flex-column flex-md-row gap-4">
|
|
<div
|
|
t-if="not use_delivery_as_billing and order._has_deliverable_products()"
|
|
t-attf-class="{{not use_delivery_as_billing and not only_services and 'col'}}"
|
|
>
|
|
<t t-if="order.pickup_location_data">
|
|
<p t-att-class="delivery_title_classes">Deliver to pickup point</p>
|
|
<div
|
|
class="fw-bold" t-out="order.pickup_location_data.get('name', '')"
|
|
/>
|
|
<div t-out="order.pickup_location_data.get('street', '')"/>
|
|
<div t-out="order.pickup_location_data.get('city', '')
|
|
+ ' ' + order.pickup_location_data.get('zip_code','')"
|
|
/>
|
|
</t>
|
|
<t t-else="">
|
|
<p t-att-class="delivery_title_classes">Delivery</p>
|
|
<t
|
|
t-out="order.partner_shipping_id"
|
|
t-options="dict(
|
|
widget='contact',
|
|
fields=['name', 'address'],
|
|
no_marker=True,
|
|
)"
|
|
/>
|
|
</t>
|
|
</div>
|
|
<div t-attf-class="{{not use_delivery_as_billing and not only_services and 'col'}}">
|
|
<p t-if="use_delivery_as_billing and not only_services" t-att-class="delivery_title_classes">Delivery & Billing</p>
|
|
<p t-else="" t-att-class="delivery_title_classes">Billing</p>
|
|
<t
|
|
t-out="order.partner_invoice_id"
|
|
t-options="dict(
|
|
widget='contact',
|
|
fields=['name', 'address'],
|
|
no_marker=True,
|
|
)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- /shop/payment route -->
|
|
<template id="payment" name="Payment">
|
|
<t t-call="website_sale.checkout_layout">
|
|
<t t-set="additional_title">Shop - Select Payment Method</t>
|
|
<t t-set='redirect' t-valuef="/shop/payment"/>
|
|
<t t-set="oe_structure">
|
|
<!-- This is the drag-and-drop area for website building blocs at the end of each
|
|
checkout page. This is append at the of the page in `checkout_layout`. The
|
|
templates created in the database to store blocs are hooked using XPath on the
|
|
`oe_struture` element ID. Therefore, we can't use dynamic IDs (like with
|
|
t-att-id) and each template needs to define a div element. -->
|
|
<div class="oe_structure" id="oe_structure_website_sale_payment_2"/>
|
|
</t>
|
|
<div class="col-12" t-if="errors">
|
|
<t t-set="hide_payment_button" t-value="True"/>
|
|
<t t-foreach="errors" t-as="error">
|
|
<div class="alert alert-danger" t-if="error" role="alert">
|
|
<h4>
|
|
<t t-out="error[0]" />
|
|
</h4>
|
|
<t t-out="error[1]" />
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<div class="oe_structure clearfix" id="oe_structure_website_sale_payment_1"/>
|
|
|
|
<div t-if="not errors and website_sale_order.amount_total" name="website_sale_non_free_cart">
|
|
<div id="payment_method" class="o_not_editable mb-3">
|
|
<t t-call="payment.form"/>
|
|
</div>
|
|
<t t-if="not (payment_methods_sudo or tokens_sudo)" t-set="hide_payment_button" t-value="True"/>
|
|
</div>
|
|
<h4 t-else="" class="mb-3">
|
|
Confirm order
|
|
</h4>
|
|
<div id="address_on_payment">
|
|
<t t-call="website_sale.address_on_checkout"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Activatable through the website editor. -->
|
|
<template id="accept_terms_and_conditions"
|
|
inherit_id="navigation_buttons"
|
|
name="Accept Terms & Conditions"
|
|
active="False">
|
|
<xpath expr="//div[@name='o_website_sale_free_cart']" position="before">
|
|
<div name="website_sale_terms_and_conditions_checkbox" class="form-check mb-2">
|
|
<input type="checkbox" id="website_sale_tc_checkbox" class="form-check-input"/>
|
|
<label for="website_sale_tc_checkbox" class="form-check-label">
|
|
I agree to the <a target="_BLANK" href="/terms">terms & conditions</a>
|
|
</label>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Template of the checkout pages. Should be called in every page of the checkout flow. -->
|
|
<template id="checkout_layout" name="Checkout layout page">
|
|
<!-- Layout customization parameters:
|
|
- show_footer: Whether to show the website footer; default: `False`.
|
|
- show_navigation_button: Whether to show the navigation buttons; default: `True`.
|
|
- show_wizard_checkout: Whether to show the wizard checkout; default: `True`.
|
|
- show_shorter_cart_summary: Whether to show the shorter cart_summary (without items
|
|
summary and with express checkout buttons) or the full;
|
|
default: `None`.
|
|
- show_mobile_cart_summary: Whether to show the mobile offcanvas cart_summary;
|
|
default: `True`.
|
|
- oe_structure: The structure element to append at the bottom of the page;
|
|
default: `None`.
|
|
-->
|
|
<!-- Checkout context (non exhaustive):
|
|
- redirect: The route to redirect to when a customer enters a coupon; default: `None`.
|
|
- website_sale_order: The current order.
|
|
-->
|
|
<t t-call="website.layout">
|
|
<t t-set="no_footer" t-value="True if show_footer is None else not show_footer"/>
|
|
<t t-set="show_navigation_button" t-value="True if show_navigation_button is None else show_navigation_button"/>
|
|
<t t-set="expand_order_summary" t-value="not show_navigation_button"/>
|
|
<t t-set="show_wizard_checkout" t-value="True if show_wizard_checkout is None else show_wizard_checkout"/>
|
|
<t
|
|
t-set="show_mobile_cart_summary"
|
|
t-value="True if show_mobile_cart_summary is None else show_mobile_cart_summary"
|
|
/>
|
|
<t t-set="body_classname" t-value="'o_website_sale_checkout'"/>
|
|
<div id="wrap" class="d-flex flex-column flex-grow-1">
|
|
<div class="oe_website_sale o_website_sale_checkout_container container d-flex flex-column flex-grow-1 py-lg-2">
|
|
<div t-attf-class="row #{not show_wizard_checkout and 'mt32'} mb32">
|
|
<div t-if="show_wizard_checkout" class="col-12">
|
|
<t t-call="website_sale.wizard_checkout"/>
|
|
</div>
|
|
<!-- Checkout Page Content | Left Column -->
|
|
<div
|
|
t-attf-class="oe_clear_stucture oe_cart col-12 #{'col-lg-7' if website_sale_order and website_sale_order.website_order_line else ''}"
|
|
>
|
|
<t t-out="0"/>
|
|
</div>
|
|
|
|
<!-- Short Checkout Summary displayed on /cart | Right Column -->
|
|
<div
|
|
t-if="show_shorter_cart_summary"
|
|
class="o_wsale_shorter_cart_summary offset-xxl-1 col-lg-5 col-xxl-4"
|
|
>
|
|
<!-- Parent element needed for proper JS side rendering -->
|
|
<t t-call="website_sale.shorter_cart_summary"/>
|
|
</div>
|
|
<!-- Full Checkout Summary displayed on all checkout pages | Right Column -->
|
|
<div
|
|
t-else=""
|
|
class="d-none d-lg-block offset-xxl-1 col-12 col-lg-5 col-xxl-4 rounded"
|
|
>
|
|
<div class="o_total_card card sticky-lg-top o_wsale_sticky_object mb-3 mb-lg-0">
|
|
<div class="card-body p-lg-4 pt-lg-3">
|
|
<div class="d-none d-lg-block">
|
|
<t t-call="website_sale.cart_summary_content"/>
|
|
<t t-call="website_sale.total">
|
|
<t
|
|
t-set="_cart_total_classes"
|
|
t-valuef="border-top pt-2"
|
|
/>
|
|
</t>
|
|
</div>
|
|
<div
|
|
t-if="show_navigation_button"
|
|
class="o_cta_navigation_container px-0"
|
|
>
|
|
<t t-call="website_sale.navigation_buttons"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Mobile Order Summary | Bottom of page and Offcanvas -->
|
|
<t t-if="not show_shorter_cart_summary and show_mobile_cart_summary">
|
|
<div class="o_mobile_summary d-lg-none sticky-bottom mt-auto bg-body">
|
|
<button
|
|
class="btn btn-light d-flex justify-content-between align-items-center gap-2 w-100 mb-2"
|
|
data-bs-toggle="offcanvas"
|
|
href="#o_cart_summary_offcanvas"
|
|
aria-controls="o_cart_summary_offcanvas"
|
|
>
|
|
Order
|
|
<span
|
|
id="amount_total_summary"
|
|
class="monetary_field ms-auto"
|
|
t-field="website_sale_order.amount_total"
|
|
t-options='{"widget": "monetary", "display_currency": website_sale_order.currency_id}'
|
|
/>
|
|
<span
|
|
class="o_cart_item_count badge ms-1 top-0 bg-primary"
|
|
t-out="str(website_sale_order.cart_quantity)"
|
|
/>
|
|
<i class="fa fa-angle-right ms-2" role="img"/>
|
|
</button>
|
|
<t
|
|
t-if="show_navigation_button"
|
|
t-call="website_sale.navigation_buttons"
|
|
/>
|
|
</div>
|
|
<div
|
|
id="o_cart_summary_offcanvas"
|
|
class="offcanvas offcanvas-end"
|
|
tabindex="-1"
|
|
aria-labelledby="o_cart_summary_offcanvas"
|
|
>
|
|
<div class="offcanvas-body">
|
|
<t t-call="website_sale.cart_summary_content"/>
|
|
</div>
|
|
<div class="offcanvas-footer p-3">
|
|
<a href="/shop/cart" class="btn btn-link w-100">
|
|
Coupon, Gift card, Promo-code?
|
|
</a>
|
|
<t t-call="website_sale.total">
|
|
<t t-set="hide_promotions" t-value="True"/>
|
|
<t
|
|
t-set="_cart_total_classes"
|
|
t-valuef="border-top px-3 pt-2 mx-n3"
|
|
/>
|
|
</t>
|
|
<button
|
|
class="btn btn-light w-100"
|
|
type="button"
|
|
data-bs-dismiss="offcanvas"
|
|
aria-label="Close"
|
|
>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<!-- This is the drag-and-drop area for website building blocs at the end of each
|
|
checkout page. The templates created in the database to store blocs are hooked
|
|
using XPath on the `oe_struture` element ID. Therefore, we can't use dynamic
|
|
IDs (like with t-att-id) and each template needs to define a div element. -->
|
|
<t t-out="oe_structure"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="website_sale.shorter_cart_summary">
|
|
<div
|
|
t-if="website_sale_order and website_sale_order.website_order_line"
|
|
class="o_total_card card o_wsale_sticky_object sticky-lg-top"
|
|
>
|
|
<div class="card-body p-0 p-lg-4" name="o_cart_total_card_body">
|
|
<t t-call="website_sale.total">
|
|
<t
|
|
t-set="_cart_total_classes"
|
|
t-valuef="o_checkout_cart_total pt-2 pt-lg-0"
|
|
/>
|
|
</t>
|
|
<t t-call="website_sale.navigation_buttons"/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.checkout_layout` and `website_sale.confirmation`. -->
|
|
<template id="website_sale.cart_summary_content">
|
|
<div
|
|
t-if="not website_sale_order or not website_sale_order.website_order_line"
|
|
name="cart_summary_info"
|
|
class="alert alert-info"
|
|
>
|
|
Your cart is empty!
|
|
</div>
|
|
<div>
|
|
<div t-att-class="len(website_sale_order.website_order_line) > 3 and 'o_wsale_scrollable_table'">
|
|
<table
|
|
t-if="website_sale_order and website_sale_order.website_order_line"
|
|
class="o_cart_products_table table mb-0"
|
|
>
|
|
<tbody>
|
|
<tr
|
|
t-foreach="website_sale_order.website_order_line"
|
|
t-as="line"
|
|
t-att-class="line_last and 'border-transparent'"
|
|
>
|
|
<td class="td-img ps-0 pt-3">
|
|
<div class="o_cart_product_image position-relative">
|
|
<span
|
|
t-if="not line._is_sellable() and line.product_id.image_128"
|
|
>
|
|
<img
|
|
t-att-src="image_data_uri(line.product_id.image_128)"
|
|
class="img rounded"
|
|
t-att-alt="line.name_short"
|
|
/>
|
|
</span>
|
|
<span
|
|
t-else=""
|
|
t-field="line.product_id.image_128"
|
|
t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded'}"
|
|
/>
|
|
<span class="o_cart_item_count badge bg-secondary position-absolute top-0 start-100 translate-middle">
|
|
<t t-out="int(line.product_uom_qty)" />
|
|
<i
|
|
t-if="line._get_shop_warning(clear=False)"
|
|
class="fa fa-warning"
|
|
role="img"
|
|
t-att-title="line._get_shop_warning()"
|
|
aria-label="Warning"
|
|
/>
|
|
</span>
|
|
</div>
|
|
</td>
|
|
<td
|
|
class="td-product_name td-qty w-100 pt-3"
|
|
name="website_sale_cart_summary_product_name"
|
|
>
|
|
<span class="text-wrap" t-out="line._get_line_header()"/>
|
|
<p class="text-muted small mb-0" t-out="line._get_combination_name()"/>
|
|
<span
|
|
t-if="line.product_template_id._has_multiple_uoms()"
|
|
class="badge mt-1 bg-light"
|
|
t-out="line.product_uom_id.name"
|
|
/>
|
|
</td>
|
|
<td class="td-price pe-0 pt-3 text-end"
|
|
name="website_sale_cart_summary_line_price">
|
|
<t t-call="website_sale.cart_product_price">
|
|
<t
|
|
t-set="product_price"
|
|
t-value="line._get_cart_display_price()"
|
|
/>
|
|
</t>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<t t-if='website_sale_order'>
|
|
<t t-set='warning' t-value='website_sale_order._get_shop_warning(clear=False)' />
|
|
<div t-if='warning' class="alert alert-warning" role="alert">
|
|
<strong>Warning!</strong> <t t-out='website_sale_order._get_shop_warning()'/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- /shop/confirmation route -->
|
|
<template id="confirmation">
|
|
<t t-call="website_sale.checkout_layout">
|
|
<t t-set="show_wizard_checkout" t-value="False"/>
|
|
<t t-set="show_mobile_cart_summary" t-value="False"/>
|
|
<t t-set="show_navigation_button" t-value="False"/>
|
|
<t t-set="show_footer" t-value="True"/>
|
|
<t t-set="additional_title">Shop - Confirmed</t>
|
|
<t t-set="hide_promotions" t-value="True"/>
|
|
<t t-set="oe_structure">
|
|
<!-- This is the drag-and-drop area for website building blocs at the end of each
|
|
checkout page. This is append at the of the page in `checkout_layout`. The
|
|
templates created in the database to store blocs are hooked using XPath on the
|
|
`oe_struture` element ID. Therefore, we can't use dynamic IDs (like with
|
|
t-att-id) and each template needs to define a div element. -->
|
|
<div class="oe_structure" id="oe_structure_website_sale_confirmation_3"/>
|
|
</t>
|
|
|
|
<t t-set="tx_sudo" t-value="order.get_portal_last_transaction()"/>
|
|
<div
|
|
t-if="tx_sudo.state in ['pending', 'done'] or not order.amount_total"
|
|
class="d-flex justify-content-between align-items-center"
|
|
>
|
|
<h3>Thank you for your order.</h3>
|
|
<a
|
|
title="Print"
|
|
role="button"
|
|
class="d-none d-md-inline-block btn btn-light ms-auto"
|
|
href="/shop/print"
|
|
target="_blank"
|
|
aria-label="Print"
|
|
>
|
|
<i class="fa fa-print me-2"/>Print
|
|
</a>
|
|
</div>
|
|
<div id="order_name" class="mb-4">
|
|
<h5>
|
|
<span>Order</span>
|
|
<span t-field="order.name"/>
|
|
</h5>
|
|
</div>
|
|
<t t-if="request.env['res.users']._get_signup_invitation_scope() == 'b2c' and request.website.is_public_user()">
|
|
<p class="alert alert-info mt-3" role="status">
|
|
<a role="button" t-att-href="order.partner_id.signup_prepare() and order.partner_id.with_context(relative_url=True)._get_signup_url()" class="btn btn-primary o_translate_inline">Sign Up</a>
|
|
<span class="align-middle">to follow your order.</span>
|
|
</p>
|
|
</t>
|
|
<div class="oe_structure clearfix mt-3" id="oe_structure_website_sale_confirmation_1"/>
|
|
<t t-call="website_sale.payment_confirmation_status"/>
|
|
<div class="mt-3">
|
|
<t t-call="website_sale.address_on_checkout"/>
|
|
</div>
|
|
<div class="oe_structure mt-3" id="oe_structure_website_sale_confirmation_2"/>
|
|
<input t-if='website.plausible_shared_key' type='hidden' class='js_plausible_push' data-event-name='Shop' t-attf-data-event-params='{"CTA": "Order Confirmed", "amount": "#{"%3s-%3s" % (max(0, round(website_sale_order.amount_total/100)*100 - 50), round(website_sale_order.amount_total/100)*100 + 50)}"}' />
|
|
<div class="d-lg-none">
|
|
<t t-call="website_sale.cart_summary_content"/>
|
|
<t t-call="website_sale.total">
|
|
<t
|
|
t-set="_cart_total_classes"
|
|
t-valuef="border-top pt-2"
|
|
/>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.checkout_layout`. -->
|
|
<template id="total">
|
|
<!-- Layout customization parameters:
|
|
- _cart_total_classes: CSS classes to append on the root div of the total template;
|
|
default `None`.
|
|
-->
|
|
<div
|
|
t-if="website_sale_order and website_sale_order.website_order_line"
|
|
t-attf-class="o_cart_total #{_cart_total_classes}"
|
|
>
|
|
<table class="table mb-0">
|
|
<tr
|
|
t-if="website_sale_order._has_deliverable_products()"
|
|
name="o_order_delivery"
|
|
>
|
|
<td class="ps-0 pt-0 pb-2 border-0 text-muted" colspan="2">
|
|
Delivery
|
|
</td>
|
|
<td class="text-end pe-0 pt-0 pb-2 border-0">
|
|
<span
|
|
name="o_message_no_dm_set"
|
|
t-att-class="'d-none' if website_sale_order.carrier_id else ''"
|
|
title="Price will be updated after choosing a delivery method"
|
|
>
|
|
-
|
|
</span>
|
|
<span
|
|
t-out="website_sale_order.amount_delivery"
|
|
t-att-class="'monetary_field' + ('' if website_sale_order.carrier_id else ' d-none')"
|
|
style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
<tr name="o_order_total_untaxed">
|
|
<td
|
|
class="border-0 pb-2 ps-0 pt-0 text-start text-muted"
|
|
colspan="2">
|
|
Subtotal
|
|
</td>
|
|
<td class="text-end border-0 pb-2 pe-0 pt-0">
|
|
<span t-field="website_sale_order.amount_untaxed"
|
|
class="monetary_field"
|
|
style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
</td>
|
|
</tr>
|
|
<tr name="o_order_total_taxes">
|
|
<td colspan="2" class="text-muted border-0 p-0 pe-3 pb-2">Taxes</td>
|
|
<td class="text-end border-0 p-0 ps-3 pb-2">
|
|
<span t-field="website_sale_order.amount_tax"
|
|
class="monetary_field"
|
|
style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
</td>
|
|
</tr>
|
|
<tr name="o_order_total" class="border-top">
|
|
<td colspan="2" class="border-0 ps-0 pt-2"><strong>Total</strong></td>
|
|
<td class="text-end border-0 px-0 pt-2">
|
|
<strong t-field="website_sale_order.amount_total"
|
|
class="monetary_field text-end p-0"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Deactivatable through the website editor. -->
|
|
<template id="reduction_code" inherit_id="website_sale.total" name="Promo Code">
|
|
<!-- Checkout context:
|
|
- hide_promotions: Whether to hide promotion input; default: `None`.
|
|
- redirect: The route to redirect to when a customer enters a coupon; default: `None`.
|
|
- website_sale_order: The current order.
|
|
-->
|
|
<xpath expr="//div[contains(@t-attf-class, 'o_cart_total')]//table/tr[last()]" position="after">
|
|
<tr t-if="not hide_promotions">
|
|
<td colspan="3" class="text-end text-xl-end border-0 p-0">
|
|
<span>
|
|
<t t-set="force_coupon" t-value="website_sale_order.pricelist_id.code"/>
|
|
<div t-if="not force_coupon" class="coupon_form">
|
|
<t t-call="website_sale.coupon_form"/>
|
|
</div>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.confirmation`. -->
|
|
<template id="payment_confirmation_status">
|
|
<div
|
|
name="order_confirmation"
|
|
class="mt-3"
|
|
t-att-data-order-id="order.id"
|
|
t-att-data-order-tracking-info="json.dumps(order_tracking_info)"
|
|
>
|
|
<t t-set="tx_sudo" t-value="order.get_portal_last_transaction()"/>
|
|
<div
|
|
t-if="tx_sudo"
|
|
t-attf-class="alert px-3 pb-0 pt-3 #{
|
|
(tx_sudo.state == 'pending' and 'alert-info') or
|
|
(tx_sudo.state == 'done' and order.amount_total == tx_sudo.amount and 'alert-success') or
|
|
(tx_sudo.state == 'done' and order.amount_total != tx_sudo.amount and 'alert-warning') or
|
|
(tx_sudo.state == 'authorized' and 'alert-success') or
|
|
'alert-danger'}"
|
|
role="alert"
|
|
>
|
|
<a
|
|
title="Edit"
|
|
class="btn btn-sm btn-link text-dark float-end"
|
|
role="button"
|
|
groups="base.group_system"
|
|
target="_blank"
|
|
aria-label="Edit"
|
|
t-attf-href="/odoo/action-payment.action_payment_provider/{{tx_sudo.provider_id.id}}"
|
|
>
|
|
<i class="fa fa-pencil"/>
|
|
</a>
|
|
<div>
|
|
<t t-if="tx_sudo.state == 'pending'">
|
|
<t t-out="tx_sudo.provider_id.sudo().pending_msg"/>
|
|
</t>
|
|
<t t-if="tx_sudo.state == 'done'">
|
|
<span t-if='tx_sudo.provider_id.sudo().done_msg' t-out="tx_sudo.provider_id.sudo().done_msg"/>
|
|
</t>
|
|
<t t-if="tx_sudo.state == 'done' and order.amount_total != tx_sudo.amount">
|
|
<span>Unfortunately your order can not be confirmed as the amount of your payment does not match the amount of your cart.
|
|
Please contact the responsible of the shop for more information.</span>
|
|
</t>
|
|
<t t-if="tx_sudo.state == 'cancel'">
|
|
<t t-out="tx_sudo.provider_id.sudo().cancel_msg"/>
|
|
</t>
|
|
<t t-if="tx_sudo.state == 'authorized'">
|
|
<t t-if="tx_sudo.provider_id.sudo().auth_msg" t-out="tx_sudo.provider_id.sudo().auth_msg"/>
|
|
<span t-else="">Your payment has been authorized.</span>
|
|
</t>
|
|
<t t-if="tx_sudo.state == 'error'">
|
|
<span t-out="tx_sudo.state_message"/>
|
|
</t>
|
|
</div>
|
|
<t t-if="tx_sudo.provider_code == 'custom'">
|
|
<div id="order_reference" t-if="order.reference" class="mt-2">
|
|
<b>Communication: </b><span t-out='order.reference'/>
|
|
</div>
|
|
<div t-if="tx_sudo.provider_id.sudo().qr_code">
|
|
<t t-set="qr_code" t-value="tx_sudo.company_id.partner_id.bank_ids[:1].build_qr_code_base64(order.amount_total,tx_sudo.reference, None, tx_sudo.currency_id, tx_sudo.partner_id)"/>
|
|
<div class="mt-2" t-if="qr_code">
|
|
<h3>Or scan me with your banking app.</h3>
|
|
<img class="border border-dark rounded" t-att-src="qr_code"/>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.brand_promotion" inherit_id="website.brand_promotion">
|
|
<xpath expr="//t[@t-call='web.brand_promotion_message']" position="replace">
|
|
<t t-call="web.brand_promotion_message">
|
|
<t t-set="_message">
|
|
The #1 <a target="_blank" href="http://www.odoo.com/app/ecommerce?utm_source=db&utm_medium=website">Open Source eCommerce</a>
|
|
</t>
|
|
<t t-set="_utm_medium" t-valuef="website"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Product page images -->
|
|
<template id="website_sale.shop_product_images" name="Shop Product Images">
|
|
<t t-set="product_images" t-value="product_variant._get_images() if product_variant else product._get_images()"/>
|
|
<t
|
|
t-set="ribbon"
|
|
t-value="product.sudo()._get_ribbon(price_vals=combination_info, variant=product_variant)"
|
|
/>
|
|
<t t-set="bg_color" t-value="ribbon.bg_color"/>
|
|
<t t-set="text_color" t-value="ribbon.text_color"/>
|
|
<t t-call="website_sale.shop_product_#{website.product_page_image_layout}"/>
|
|
</template>
|
|
|
|
<template id="website_sale.shop_product_image">
|
|
<span
|
|
t-attf-class="o_ribbons o_not_editable #{ribbon._get_css_classes()} z-1"
|
|
t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}"
|
|
t-att-data-ribbon-id="ribbon.id"
|
|
t-out="ribbon.name or ''"
|
|
/>
|
|
<div
|
|
t-if="product_image._name == 'product.image' and product_image.embed_code"
|
|
t-att-class="image_classes + ' ratio ratio-16x9'"
|
|
>
|
|
<t t-out="product_image.embed_code"/>
|
|
</div>
|
|
<div
|
|
t-elif="len(product_images) == 1 and website.product_page_image_layout != 'grid'"
|
|
class="position-relative d-inline-flex overflow-hidden m-auto h-100 w-100"
|
|
>
|
|
<span
|
|
t-field="product_image.image_1920"
|
|
name="o_img_with_max_suggested_width"
|
|
class="o_product_detail_img_wrapper d-flex align-items-start justify-content-center h-100 w-100 oe_unmovable"
|
|
t-options="{
|
|
'widget': 'image',
|
|
'preview_image': 'image_1024',
|
|
'class': 'oe_unmovable product_detail_img h-100 w-100',
|
|
'alt-field': 'name',
|
|
'zoom': product_image.can_image_1024_be_zoomed and 'image_1920',
|
|
}"
|
|
/>
|
|
|
|
</div>
|
|
<div
|
|
t-else=""
|
|
t-field="product_image.image_1920"
|
|
t-att-class="image_classes + ' o_product_detail_img_wrapper oe_unmovable'"
|
|
t-attf-style="{{
|
|
image_natural_index and ('--o-wsale-grid-product-img-natural-order: ' + str(image_natural_index)) or false
|
|
}}"
|
|
t-options="{
|
|
'widget': 'image',
|
|
'preview_image': 'image_1024',
|
|
'class': 'oe_unmovable product_detail_img w-100 h-100',
|
|
'alt-field': 'name',
|
|
'zoom': product_image.can_image_1024_be_zoomed and 'image_1920',
|
|
}"
|
|
/>
|
|
</template>
|
|
|
|
<!-- Product page images: Carousel -->
|
|
<template id="website_sale.shop_product_carousel" name="Shop Product Carousel">
|
|
<t t-set="product_carousel_block_name">Product Carousel</t>
|
|
<div
|
|
id="o-carousel-product"
|
|
t-attf-class="#{len(product_images) > 1 and 'o_carousel_not_single'} carousel slide mb-3 overflow-hidden"
|
|
t-att-data-name="product_carousel_block_name"
|
|
>
|
|
<div
|
|
class="o_carousel_product_outer carousel-outer position-relative d-flex align-items-center w-100 overflow-hidden"
|
|
>
|
|
<span
|
|
t-if="len(product_images) > 1"
|
|
t-attf-class="o_ribbons #{ribbon._get_css_classes()} z-1"
|
|
t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}"
|
|
t-out="ribbon.name or ''"
|
|
t-att-data-ribbon-id="ribbon.id"
|
|
/>
|
|
<div class="carousel-inner h-100">
|
|
<t t-set="image_classes" t-value="'d-flex align-items-center justify-content-center h-100'"/>
|
|
<t t-foreach="product_images" t-as="product_image">
|
|
<div t-attf-class="carousel-item h-100 w-100 text-center#{' active' if product_image_first else ''}">
|
|
<t t-call="website_sale.shop_product_image"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<t t-if="len(product_images) > 1">
|
|
<a class="carousel-control-prev" href="#o-carousel-product" role="button" data-bs-slide="prev">
|
|
<i
|
|
class="oi oi-chevron-left oe_unmovable border bg-white text-900"
|
|
role="img"
|
|
aria-label="Previous"
|
|
title="Previous"
|
|
/>
|
|
</a>
|
|
<a class="carousel-control-next" href="#o-carousel-product" role="button" data-bs-slide="next">
|
|
<i
|
|
class="oi oi-chevron-right oe_unmovable border bg-white text-900"
|
|
role="img"
|
|
aria-label="Next"
|
|
title="Next"
|
|
/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="carousel_product_indicators_bottom" inherit_id="website_sale.shop_product_carousel" name="Carousel Product Indicators Bottom" active="False">
|
|
<xpath expr="//div[hasclass('o_carousel_product_outer')]" position="after">
|
|
<t t-call="website_sale.carousel_product_indicators">
|
|
<t t-set="indicators_div_class" t-value="'pt-2 overflow-hidden'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="carousel_product_indicators_left" inherit_id="website_sale.shop_product_carousel" name="Carousel Product Indicators Left">
|
|
<xpath expr="//div[hasclass('o_carousel_product_outer')]" position="before">
|
|
<t t-call="website_sale.carousel_product_indicators">
|
|
<t t-set="indicators_list_class" t-value="'d-flex d-lg-block pe-lg-2'"/>
|
|
</t>
|
|
</xpath>
|
|
<xpath expr="//div[@id='o-carousel-product']" position="attributes">
|
|
<attribute name="t-attf-class" add="o_carousel_product_left_indicators d-flex" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="carousel_product_indicators" name="Carousel Product">
|
|
<div
|
|
t-if="len(product_images) > 1"
|
|
t-ignore="True"
|
|
t-attf-class="o_carousel_product_indicators {{indicators_div_class}}"
|
|
>
|
|
<ol
|
|
t-attf-class="carousel-indicators position-static pt-2 pt-lg-0 mx-auto my-0 {{indicators_list_class}} {{website.product_page_image_width == '100_pc' and 'justify-content-center'}}"
|
|
>
|
|
<li
|
|
t-foreach="product_images" t-as="product_image"
|
|
t-attf-class="align-top position-relative {{'active' if product_image_first else ''}}"
|
|
data-bs-target="#o-carousel-product"
|
|
t-att-data-bs-slide-to="str(product_image_index)"
|
|
>
|
|
<div
|
|
t-field="product_image.image_128"
|
|
t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'object-fit-cover w-100 h-100', 'alt-field': 'name'}"
|
|
/>
|
|
<i
|
|
t-if="product_image._name == 'product.image' and product_image.embed_code"
|
|
class="fa fa-2x fa-play-circle o_product_video_thumb bg-black-50 text-center"
|
|
/>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Product page images: Grid -->
|
|
<template id="website_sale.shop_product_grid" name="Shop Product Grid">
|
|
<t t-set="imagesCount" t-value="len(product_images)"/>
|
|
<div
|
|
id="o-grid-product"
|
|
t-attf-class="
|
|
o_grid_uses_ratio_{{website.product_page_image_ratio}}
|
|
o_grid_uses_ratio_mobile_{{website.product_page_image_ratio_mobile}}
|
|
{{
|
|
imagesCount > 11 and 'o_grid_has_more_than_11' or
|
|
imagesCount > 9 and 'o_grid_has_more_than_9' or
|
|
imagesCount > 5 and 'o_grid_has_more_than_5' or
|
|
imagesCount > 3 and 'o_grid_has_more_than_3' or
|
|
imagesCount == 1 and 'o_grid_solo' or ''
|
|
}}
|
|
"
|
|
data-name="Product Grid"
|
|
t-att-data-image_spacing="website.product_page_image_spacing"
|
|
t-att-data-image_count="len(product_images)"
|
|
t-att-data-grid_columns="website.product_page_grid_columns"
|
|
>
|
|
<div class="position-relative overflow-hidden">
|
|
<span
|
|
t-attf-class="o_ribbons #{ribbon._get_css_classes()}"
|
|
t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}"
|
|
t-out="ribbon.name or ''"
|
|
t-att-data-ribbon-id="ribbon.id"
|
|
/>
|
|
<!-- One row for every two images -->
|
|
<t t-set="image_classes" t-value="'w-100'"/>
|
|
<div
|
|
t-attf-class="o_wsale_product_page_grid_row {{website._get_product_page_grid_image_spacing_classes()}}"
|
|
>
|
|
<t t-foreach="website.product_page_grid_columns" t-as="col_idx">
|
|
<t t-set="col_feed_counter" t-value="0"/>
|
|
|
|
<div
|
|
t-if="col_idx_index < len(product_images)"
|
|
t-attf-class="o_wsale_product_page_grid_column flex-column px-0"
|
|
>
|
|
<t t-foreach="len(product_images)" t-as="image_idx">
|
|
<t
|
|
t-set="product_image"
|
|
t-value="image_idx < len(product_images) and product_images[image_idx] or False"
|
|
/>
|
|
<t
|
|
t-if="product_image and col_idx == col_feed_counter"
|
|
t-call="website_sale.shop_product_image"
|
|
>
|
|
<t t-set="image_natural_index" t-value="image_idx"/>
|
|
<t
|
|
t-set="image_classes"
|
|
t-value="image_idx_last and 'o_product_detail_img_wrapper_last' or ''"
|
|
/>
|
|
</t>
|
|
<t t-if="col_feed_counter == (website.product_page_grid_columns - 1)">
|
|
<t t-set="col_feed_counter" t-value="0"/>
|
|
</t>
|
|
<t t-else="">
|
|
<t t-set="col_feed_counter" t-value="col_feed_counter + 1"/>
|
|
</t>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="ecom_show_extra_fields" inherit_id="website_sale.product" active="True" name="Show Extra Fields">
|
|
<xpath expr="//div[@id='product_details']" position="inside">
|
|
<t t-if="any([product.sudo()[field.name] for field in website.shop_extra_field_ids])">
|
|
<hr/>
|
|
<p class="text-muted">
|
|
<t t-foreach="website.shop_extra_field_ids" t-as='field' t-if="product.sudo()[field.name]">
|
|
<b><t t-out='field.label'/>: </b>
|
|
<t t-if='field.field_id.ttype != "binary"'>
|
|
<span t-out="product.sudo()[field.name]" t-options="{'widget': field.field_id.ttype}"/>
|
|
</t>
|
|
<t t-else=''>
|
|
<a target='_blank' t-attf-href='/web/content/product.template/#{product.id}/#{field.name}?download=1'>
|
|
<i class='fa fa-file'></i>
|
|
</a>
|
|
</t>
|
|
<br/>
|
|
</t>
|
|
</p>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
</odoo>
|