2744 lines
176 KiB
XML
2744 lines
176 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<template id="header_cart_link" name="Header Cart Link">
|
|
<t t-nocache="The number of products is dynamic, this rendering cannot be cached."
|
|
t-nocache-_icon="_icon"
|
|
t-nocache-_text="_text"
|
|
t-nocache-_badge="_badge"
|
|
t-nocache-_badge_class="_badge_class"
|
|
t-nocache-_icon_wrap_class="_icon_wrap_class"
|
|
t-nocache-_text_class="_text_class"
|
|
t-nocache-_item_class="_item_class"
|
|
t-nocache-_link_class="_link_class">
|
|
<t t-set="website_sale_cart_quantity" t-value="request.session['website_sale_cart_quantity'] if 'website_sale_cart_quantity' in request.session else website.sale_get_order().cart_quantity or 0"/>
|
|
<t t-set="show_cart" t-value="true"/>
|
|
<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 text-bg-primary #{_badge_class} #{'d-none' if (website_sale_cart_quantity == 0) else ''}" t-esc="website_sale_cart_quantity" t-att-data-order-id="request.session.get('sale_order_id', '')"/>
|
|
</div>
|
|
<span t-if="_text" t-attf-class="#{_text_class}">My Cart</span>
|
|
</a>
|
|
</li>
|
|
</t>
|
|
</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>
|
|
|
|
<template id="template_header_mobile" inherit_id="website.template_header_mobile">
|
|
<xpath expr="//ul[hasclass('o_header_mobile_buttons_wrap')]//li" 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_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_hover 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="'position-relative me-2 rounded-circle border p-2 bg-o-color-3 o_border_contrast'"/>
|
|
<t t-set="_link_class" t-value="'btn d-flex align-items-center fw-bold text-reset o_navlink_background_hover'"/>
|
|
<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 btn btn-sm 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_hover 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">
|
|
<div class="d-flex 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_hover 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>
|
|
</div>
|
|
</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('/shop'+ ('/category/'+slug(category)) if category else None, search=0) or '/shop'"/>
|
|
<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 t-if="attrib_values">
|
|
<t t-foreach="attrib_values" t-as="a">
|
|
<input type="hidden" name="attrib" t-att-value="'%s-%s' % (a[0], a[1])" />
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="products_item" name="Products item">
|
|
<form action="/shop/cart/update" method="post" class="oe_product_cart h-100 d-flex"
|
|
t-att-data-publish="product.website_published and 'on' or 'off'"
|
|
itemscope="itemscope" itemtype="http://schema.org/Product">
|
|
|
|
<t t-set="product_href" t-value="keep(product.website_url, page=(pager['page']['num'] if pager['page']['num']>1 else None))" />
|
|
<t t-set="image_type" t-value="product._get_suitable_image_size(ppr, td_product['x'], td_product['y'])"/>
|
|
|
|
<div class="oe_product_image position-relative h-100 flex-grow-0 overflow-hidden">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
|
<a t-att-href="product_href" class="oe_product_image_link d-block h-100 position-relative" itemprop="url" contenteditable="false">
|
|
<t t-set="image_holder" t-value="product._get_image_holder()"/>
|
|
<span t-field="image_holder.image_1920"
|
|
t-options="{'widget': 'image', 'preview_image': image_type, 'itemprop': 'image', 'class': 'h-100 w-100 position-absolute'}"
|
|
class="oe_product_image_img_wrapper d-flex h-100 justify-content-center align-items-center position-absolute"/>
|
|
|
|
<t t-set="bg_color" t-value="td_product['ribbon']['bg_color'] or ''"/>
|
|
<t t-set="text_color" t-value="td_product['ribbon']['text_color']"/>
|
|
<t t-set="bg_class" t-value="td_product['ribbon']['html_class']"/>
|
|
<span t-attf-class="o_ribbon o_not_editable #{bg_class}" t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}" t-out="td_product['ribbon']['html'] or ''"/>
|
|
</a>
|
|
</div>
|
|
<div class="o_wsale_product_information position-relative d-flex flex-column flex-grow-1 flex-shrink-1">
|
|
<div class="o_wsale_product_information_text flex-grow-1">
|
|
<h6 class="o_wsale_products_item_title mb-2">
|
|
<a class="text-primary text-decoration-none" itemprop="name" t-att-href="product_href" t-att-content="product.name" t-field="product.name" />
|
|
<a t-if="not product.website_published" role="button" t-att-href="product_href" class="btn btn-sm btn-danger" title="This product is unpublished.">
|
|
Unpublished
|
|
</a>
|
|
</h6>
|
|
</div>
|
|
<div class="o_wsale_product_sub d-flex justify-content-between align-items-end gap-2 flex-wrap pb-1">
|
|
<t t-set="template_price_vals" t-value="get_product_prices(product)"/>
|
|
<div class="o_wsale_product_btn"/>
|
|
<div class="product_price" itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer">
|
|
<t t-if="'base_price' in template_price_vals and (template_price_vals['base_price'] > template_price_vals['price_reduce']) and (template_price_vals['price_reduce'] or not website.prevent_zero_price_sale)">
|
|
<del t-attf-class="text-muted me-1 h6 mb-0" style="white-space: nowrap;">
|
|
<em class="small" t-esc="template_price_vals['base_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}" />
|
|
</del>
|
|
</t>
|
|
<span class="h6 mb-0" t-if="template_price_vals['price_reduce'] or not website.prevent_zero_price_sale" t-esc="template_price_vals['price_reduce']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
<span class="h6 mb-0" t-elif="any(ptav.price_extra for ptav in product.attribute_line_ids.product_template_value_ids)">&nbsp;</span>
|
|
<span class="h6 mb-0" t-else="" t-field="website.prevent_zero_price_sale_text"/>
|
|
<span itemprop="price" style="display:none;" t-esc="template_price_vals['price_reduce']" />
|
|
<span itemprop="priceCurrency" style="display:none;" t-esc="website.currency_id.name" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
|
|
<template id="products_description" inherit_id="website_sale.products_item" active="False" name="Product Description">
|
|
<xpath expr="//*[hasclass('o_wsale_products_item_title')]" position="after">
|
|
<div class="oe_subdescription mb-2 text-muted small" contenteditable="false">
|
|
<div itemprop="description" t-field="product.description_sale"/>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="products_add_to_cart" inherit_id="website_sale.products_item" active="False" name="Add to Cart">
|
|
<xpath expr="//div[hasclass('o_wsale_product_btn')]" position="inside">
|
|
<t t-set="product_variant_id" t-value="product._get_first_possible_variant_id()"/>
|
|
<input name="product_id" t-att-value="product_variant_id" type="hidden"/>
|
|
<t t-if="product_variant_id and template_price_vals['price_reduce'] or not website.prevent_zero_price_sale">
|
|
<a t-if="product._website_show_quick_add()"
|
|
href="#" role="button" class="btn btn-primary a-submit" aria-label="Shopping cart" title="Shopping cart">
|
|
<span class="fa fa-shopping-cart"/>
|
|
</a>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="pricelist_list" name="Pricelists Dropdown">
|
|
<div t-attf-class="o_pricelist_dropdown dropdown #{_classes if hasPricelistDropdown else 'd-none'}">
|
|
<t t-set="curr_pl" t-value="website.pricelist_id" />
|
|
|
|
<a role="button" href="#" t-attf-class="dropdown-toggle btn btn-{{navClass}}" data-bs-toggle="dropdown">
|
|
<t t-esc="curr_pl and curr_pl.name or ' - '" />
|
|
</a>
|
|
<div class="dropdown-menu" role="menu">
|
|
<t t-foreach="website_sale_pricelists" t-as="pl">
|
|
<a role="menuitem" t-att-href="'/shop/change_pricelist/%s' % pl.id" class="dropdown-item">
|
|
<span class="switcher_pricelist small" t-att-data-pl_id="pl.id" t-esc="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 href="/shop">Products</a>
|
|
</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="" class="breadcrumb-item">
|
|
<a t-att-href="keep('/shop/category/%s' % slug(cat), category=0)" 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">Shop</t>
|
|
<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_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_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="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', '')]"/>
|
|
|
|
<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 class="container oe_website_sale pt-2">
|
|
<div class="row o_wsale_products_main_row align-items-start flex-nowrap">
|
|
<aside t-if="hasLeftColumn" id="products_grid_before" class="d-none d-lg-block position-sticky col-3 px-3 clearfix">
|
|
<div class="o_wsale_products_grid_before_rail vh-100 ms-n2 mt-n2 pt-2 pe-lg-2 pb-lg-5 ps-2 overflow-y-scroll">
|
|
<div t-if="opt_wsale_categories" class="products_categories mb-3">
|
|
<t t-call="website_sale.products_categories_list"/>
|
|
</div>
|
|
<div class="products_attributes_filters"/>
|
|
<t t-if="opt_wsale_filter_price and opt_wsale_attributes"
|
|
t-call="website_sale.filter_products_price"/>
|
|
</div>
|
|
</aside>
|
|
<div id="products_grid"
|
|
t-attf-class="#{'o_wsale_layout_list' if layout_mode == 'list' else ''} {{'col-lg-9' if hasLeftColumn else 'col-12'}}">
|
|
<t t-call="website_sale.products_breadcrumb">
|
|
<t t-set="_classes" t-valuef="d-none d-lg-flex w-100 p-0 small"/>
|
|
</t>
|
|
<div class="products_header btn-toolbar flex-nowrap align-items-center justify-content-between gap-3 mb-3">
|
|
<t t-if="is_view_active('website_sale.search')" t-call="website_sale.search">
|
|
<t t-set="search" t-value="original_search or search"/>
|
|
<t t-set="_form_classes" t-valuef="d-lg-inline {{'d-inline' if not category else 'd-none'}}"/>
|
|
</t>
|
|
|
|
<t t-call="website_sale.pricelist_list" t-cache="pricelist">
|
|
<t t-set="_classes" t-valuef="d-none d-lg-inline"/>
|
|
</t>
|
|
|
|
<t t-if="is_view_active('website_sale.sort')" t-call="website_sale.sort">
|
|
<t t-set="_classes" t-valuef="d-none me-auto d-lg-inline-block"/>
|
|
</t>
|
|
|
|
<div t-if="category" class="d-flex align-items-center d-lg-none me-auto">
|
|
<t t-if="not category.parent_id" t-set="backUrl" t-valuef="/shop"/>
|
|
<t t-else="" t-set="backUrl" t-value="keep('/shop/category/' + slug(category.parent_id), category=0)"/>
|
|
|
|
<a t-attf-class="btn btn-{{navClass}} me-2" t-att-href="category.parent_id and keep('/shop/category/' + slug(category.parent_id), category=0) or '/shop'">
|
|
<i class="fa fa-angle-left"/>
|
|
</a>
|
|
<h4 t-out="category.name" class="mb-0 me-auto"/>
|
|
</div>
|
|
|
|
<t t-if="is_view_active('website_sale.add_grid_or_list_option')" t-call="website_sale.add_grid_or_list_option">
|
|
<t t-set="_classes" t-valuef="d-flex"/>
|
|
</t>
|
|
|
|
<button t-if="is_view_active('website_sale.sort') or opt_wsale_categories or opt_wsale_attributes or opt_wsale_attributes_top"
|
|
t-attf-class="btn btn-{{navClass}} position-relative {{not opt_wsale_attributes_top and 'd-lg-none'}}"
|
|
data-bs-toggle="offcanvas"
|
|
data-bs-target="#o_wsale_offcanvas">
|
|
<i class="fa fa-sliders"/>
|
|
<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>
|
|
|
|
<t t-if="opt_wsale_categories_top" t-call="website_sale.filmstrip_categories"/>
|
|
|
|
<div t-if="original_search and products" class="alert alert-warning mt8">
|
|
No results found for '<span t-esc="original_search"/>'. Showing results for '<span t-esc="search"/>'.
|
|
</div>
|
|
|
|
<t t-if="category">
|
|
<t t-set='editor_msg'>Drag building blocks here to customize the header for "<t t-esc='category.name'/>" category.</t>
|
|
<div class="mb16" id="category_header" t-att-data-editor-message="editor_msg" t-field="category.website_description"/>
|
|
</t>
|
|
|
|
<div t-if="products" class="o_wsale_products_grid_table_wrapper pt-3 pt-lg-0">
|
|
<table class="table table-borderless h-100 m-0" t-att-data-ppg="ppg" t-att-data-ppr="ppr" t-att-data-default-sort="website.shop_default_sort" t-att-data-name="grid_block_name">
|
|
<colgroup t-ignore="true">
|
|
<!-- Force the number of columns (useful when only one row of (x < ppr) products) -->
|
|
<col t-foreach="ppr" t-as="p"/>
|
|
</colgroup>
|
|
<tbody>
|
|
<tr t-foreach="bins" t-as="tr_product">
|
|
<t t-foreach="tr_product" t-as="td_product">
|
|
<t t-if="td_product">
|
|
<!-- We use t-attf-class here to allow easier customization -->
|
|
<td t-att-colspan="td_product['x'] != 1 and td_product['x']"
|
|
t-att-rowspan="td_product['y'] != 1 and td_product['y']"
|
|
t-attf-class="oe_product"
|
|
t-att-data-ribbon-id="td_product['ribbon'].id"
|
|
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="product" t-value="td_product['product']"/>
|
|
</t>
|
|
</div>
|
|
</td>
|
|
</t>
|
|
<td t-else=""/>
|
|
</t>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div t-nocache="get the actual search" t-else="" class="text-center text-muted mt128 mb256">
|
|
<t t-if="not search">
|
|
<h3 class="mt8">No product defined</h3>
|
|
<p t-if="category">No product defined in this category.</p>
|
|
</t>
|
|
<t t-else="">
|
|
<h3 class="mt8">No results</h3>
|
|
<p>No results for "<strong t-esc='search'/>"<t t-if="category"> in category "<strong t-esc="category.display_name"/>"</t>.</p>
|
|
</t>
|
|
<p t-ignore="true" groups="sales_team.group_sale_manager">Click <i>'New'</i> in the top-right corner to create your first product.</p>
|
|
</div>
|
|
<div class="products_pager d-flex justify-content-center pt-5 pb-3">
|
|
<t t-call="website.pager"/>
|
|
</div>
|
|
</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>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Add the fiscal position in the t-cache key after all overrides -->
|
|
<template id="products_fiscal_position" inherit_id="website_sale.products" priority="99">
|
|
<!-- TODO: Remove this template in master -->
|
|
</template>
|
|
|
|
<!-- (Option) Products: Enable "Card" or "Thumbnails" designs -->
|
|
<template id="products_design_card" name="Card Design" inherit_id="website_sale.products" active="False">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_design_cards" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
<template id="products_design_thumbs" name="Thumbnails Design" inherit_id="website_sale.products" active="False">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_design_thumbs" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
<template id="products_design_grid" name="Grid Design" inherit_id="website_sale.products" active="False">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_design_grid" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- (Options) Products: Define products' default image ratio -->
|
|
<template id="products_thumb_4_3" name="thumb_4_3" inherit_id="website_sale.products" active="False">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_context_thumb_4_3" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
<template id="products_thumb_4_5" name="thumb_4_5" inherit_id="website_sale.products" active="False">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_context_thumb_4_5" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
<template id="products_thumb_2_3" name="thumb_2_3" inherit_id="website_sale.products" active="False">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_context_thumb_2_3" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- (Option) Products: Define products' images filling mode -->
|
|
<template id="products_thumb_cover" name="thumb_cover" inherit_id="website_sale.products" active="True">
|
|
<xpath expr="//table" position="attributes">
|
|
<attribute name="class" add="o_wsale_context_thumb_cover" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="website_sale.sort" name="Sort-by Template">
|
|
<div t-attf-class="o_sortby_dropdown dropdown dropdown_sorty_by {{_classes}}">
|
|
<small class="d-none d-lg-inline text-muted">Sort By:</small>
|
|
<a role="button" href="#" t-attf-class="dropdown-toggle btn btn-{{navClass}}" data-bs-toggle="dropdown">
|
|
<span class="d-none d-lg-inline">
|
|
<t t-if="isSortingBy" t-out="isSortingBy[0][1]"/>
|
|
<span t-else="1" t-field="website.shop_default_sort"/>
|
|
</span>
|
|
<i class="fa fa-sort-amount-asc d-lg-none"/>
|
|
</a>
|
|
<div class="dropdown-menu dropdown-menu-end" role="menu">
|
|
<t t-foreach="website_sale_sortable" t-as="sortby">
|
|
<a role="menuitem" rel="noindex,nofollow" t-att-href="keep('/shop', order=sortby[0])" class="dropdown-item">
|
|
<span t-out="sortby[1]"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.add_grid_or_list_option" active="True" name="Grid or List button">
|
|
<t t-set="_activeClasses" t-translation="off">active</t>
|
|
<div t-attf-class="o_wsale_apply_layout btn-group {{_classes}}" t-att-data-active-classes="_activeClasses">
|
|
<input type="radio" class="btn-check" name="wsale_products_layout" id="o_wsale_apply_grid" t-att-checked="'checked' if layout_mode != 'list' else None" value="grid"/>
|
|
<label t-attf-class="btn btn-{{navClass}} #{_activeClasses if layout_mode != 'list' else None} o_wsale_apply_grid" title="Grid" for="o_wsale_apply_grid">
|
|
<i class="fa fa-th-large"/>
|
|
</label>
|
|
<input type="radio" class="btn-check" name="wsale_products_layout" id="o_wsale_apply_list" t-att-checked="'checked' if layout_mode == 'list' else None" value="list"/>
|
|
<label t-attf-class="btn btn-{{navClass}} #{_activeClasses if layout_mode == 'list' else None} o_wsale_apply_list" title="List" for="o_wsale_apply_list">
|
|
<i class="oi oi-view-list"/>
|
|
</label>
|
|
</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="o_wsale_offcanvas_color_attribute" name="Color type attribute 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 mb-1 #{'active' if v.id in attrib_set else ''}"
|
|
>
|
|
<input type="checkbox"
|
|
name="attrib"
|
|
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>
|
|
|
|
<!-- 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="category" class="offcanvas-body d-lg-none flex-grow-0 overflow-visible">
|
|
<t t-call="website_sale.search">
|
|
<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">
|
|
<div class="d-block d-lg-none accordion-item" t-if="hasPricelistDropdown">
|
|
<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_pricelist"
|
|
aria-expanded="false"
|
|
aria-controls="o_wsale_offcanvas_pricelist">
|
|
<b>Pricelist</b>
|
|
</button>
|
|
</h2>
|
|
<t t-set="curr_pl" t-value="website.pricelist_id"/>
|
|
<div id="o_wsale_offcanvas_pricelist"
|
|
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_pricelists" t-as="pl"
|
|
role="menuitem"
|
|
rel="noindex,nofollow"
|
|
t-att-href="'/shop/change_pricelist/%s' % pl.id"
|
|
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='/shop/change_pricelist/#{pl.id}';"
|
|
class="form-check-input o_not_editable"
|
|
name="wsale_pricelist_radios_offcanvas"
|
|
t-att-checked="curr_pl == pl">
|
|
<label class="form-check-label fw-normal" t-out="pl.name"/>
|
|
</input>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div t-if="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('/shop', 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('/shop', 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">
|
|
<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>
|
|
|
|
<form t-if="opt_wsale_attributes or opt_wsale_attributes_top"
|
|
t-attf-class="js_attributes d-flex flex-column"
|
|
method="get">
|
|
<input t-if="category" type="hidden" name="category" t-att-value="category.id"/>
|
|
<input type="hidden" name="search" t-att-value="search"/>
|
|
|
|
<t t-foreach="attributes" t-as="a">
|
|
<t t-cache="a,attrib_set">
|
|
<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'"/>
|
|
|
|
<div t-if="a.value_ids and len(a.value_ids) > 1"
|
|
t-attf-class="accordion-item border-top-0 {{(_status == 'active') and 'order-1' or 'order-2'}}">
|
|
<h2 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 {{ not attrib_values and '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="_status == 'active' and 'True' or 'False'"
|
|
t-attf-aria-controls="o_wsale_offcanvas_attribute_{{a.id}}">
|
|
<b t-out="a.name"/>
|
|
</button>
|
|
</h2>
|
|
<div t-attf-id="o_wsale_offcanvas_attribute_{{a.id}}"
|
|
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_attribute_{{a.id}}_header">
|
|
|
|
<div class="accordion-body pt-0">
|
|
<div t-if="a.display_type == 'color'" class="pt-1 pb-3">
|
|
<t t-call="website_sale.o_wsale_offcanvas_color_attribute"/>
|
|
</div>
|
|
<div t-elif="a.display_type in ('radio', 'pills', 'select', 'multi')"
|
|
class="list-group list-group-flush">
|
|
<div t-foreach="a.value_ids" t-as="v" class="list-group-item border-0 ps-0 pb-0">
|
|
<div class="form-check mb-1">
|
|
<input type="checkbox"
|
|
name="attrib"
|
|
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="'checked' if v.id in attrib_set else None"/>
|
|
<label class="form-check-label fw-normal" t-att-for="'%s-%s' % (a.id,v.id)" t-field="v.name"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<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>
|
|
</form>
|
|
|
|
<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 px-4 border-top"/>
|
|
<t t-set="_classes_title" t-valuef="ms-n1 pt-3 pb-2"/>
|
|
</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'"
|
|
href="/shop"
|
|
title="Clear Filters">
|
|
Clear Filters
|
|
</a>
|
|
</div>
|
|
</aside>
|
|
</template>
|
|
|
|
|
|
<!-- Top-Nav Categories -->
|
|
<template id="website_sale.filmstrip_categories" name="Categories Filmstrip">
|
|
<t t-if="category.id">
|
|
<t t-set="entries" t-value="not search and category.child_id or category.child_id.filtered(lambda c: category.id in search_categories_ids)"/>
|
|
|
|
<t t-if="not entries">
|
|
<t t-set="parent" t-value="category.parent_id"/>
|
|
<t t-set="entries" t-value="not search and parent.child_id or parent.child_id.filtered(lambda c: parent.id in search_categories_ids)"/>
|
|
</t>
|
|
</t>
|
|
<t t-else="">
|
|
<t t-set="entries" t-value="categories"/>
|
|
</t>
|
|
|
|
<div t-if="entries" class="o_wsale_filmstip_container d-flex align-items-stretch mb-2 overflow-hidden">
|
|
<div class="o_wsale_filmstip_wrapper pb-1 overflow-auto">
|
|
<ul class="o_wsale_filmstip d-flex align-items-stretch mb-0 list-unstyled overflow-visible">
|
|
<t t-foreach="entries" t-as="c" t-if="c.image_128" t-set="atLeastOneImage" t-value="True"/>
|
|
<t t-if="category.parent_id" t-set="backUrl" t-value="keep('/shop/category/' + slug(category.parent_id), category=0)"/>
|
|
<t t-else="" t-set="backUrl" t-value="'/shop'"/>
|
|
|
|
<li t-foreach="entries" t-as="c"
|
|
t-attf-class="d-flex {{'pe-3' if not c_last else ''}}"
|
|
t-att-data-link-href="keep('/shop/category/' + slug(c), category=0)">
|
|
<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"/>
|
|
|
|
<div t-attf-class=" btn btn-{{navClass}} d-flex align-items-center {{'ps-2 pe-3' if c.image_128 else 'px-4'}} fs-6 fw-normal {{ 'border-primary' if c.id == category.id else '' }}" t-att-for="c.id">
|
|
<div t-if="c.image_128"
|
|
t-attf-style="background-image:url('data:image/png;base64,#{c.image_128}')"
|
|
class="o_image_40_cover oe_img_bg o_bg_img_center rounded-3 me-3"
|
|
t-att-alt="c.name "/>
|
|
<span t-field="c.name"/>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Add to cart button-->
|
|
<template id="categories_recursive" name="Category list">
|
|
<li class="nav-item mb-1">
|
|
<t t-call="website_sale.categorie_link"/>
|
|
<ul t-if="c.child_id" class="nav flex-column nav-hierarchy mt-1 ps-3">
|
|
<t t-foreach="c.child_id" 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">
|
|
<t t-if="isOffcanvas" t-set="parentCategoryId" t-valuef="offcanvas"/>
|
|
|
|
<div t-att-data-link-href="keep('/shop/category/' + slug(c), category=0)" class="form-check d-inline-block">
|
|
<input type="radio" t-attf-name="wsale_categories_radios_{{parentCategoryId}}" class="form-check-input pe-none" t-att-id="c.id" t-att-value="c.id" t-att-checked="'true' if c.id == category.id else None"/>
|
|
<label class="form-check-label fw-normal" t-att-for="c.id" t-field="c.name"/>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="website_sale.products_categories_list" active="True" name="eCommerce Categories">
|
|
<h6 t-attf-class="o_categories_collapse_title mb-3 {{_titleClasses}}"><b>Categories</b></h6>
|
|
|
|
<div class="wsale_products_categories_list">
|
|
<ul class="nav d-flex flex-column my-2">
|
|
<li class="nav-item mb-1">
|
|
<div t-att-data-link-href="keep('/shop', category=0)" class="form-check d-inline-block">
|
|
<input type="radio" t-attf-name="wsale_categories_radios{{_radioGroup}}" class="form-check-input pe-none o_not_editable" t-att-id="all_products" t-att-value="all_products" t-att-checked="'true' if not category else None"/>
|
|
<label class="form-check-label fw-normal" t-att-for="all_products">All Products</label>
|
|
</div>
|
|
</li>
|
|
<t t-foreach="categories" t-as="c">
|
|
<t t-call="website_sale.categories_recursive"/>
|
|
</t>
|
|
</ul>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="option_collapse_categories_recursive" name="Collapse Category Recursive">
|
|
<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)"/>
|
|
|
|
<t t-if="children">
|
|
<t t-set="isOpen" t-value="c.id in category.parents_and_self.ids"/>
|
|
|
|
<li class="nav-item">
|
|
<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="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 pb-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 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="//div[hasclass('wsale_products_categories_list')]" position="attributes">
|
|
<attribute name="class" add="o_shop_collapse_category" separator=" "/>
|
|
</xpath>
|
|
<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[hasclass('products_attributes_filters')]" position="inside">
|
|
<div id="wsale_products_attributes_collapse"
|
|
class=" position-relative">
|
|
<form t-if="attributes or all_tags" class="js_attributes position-relative mb-2" method="get">
|
|
<input t-if="category" type="hidden" name="category" t-att-value="category.id" />
|
|
<input type="hidden" name="search" t-att-value="search" />
|
|
<input type="hidden" name="order" t-att-value="order"/>
|
|
<a t-if="attrib_values or tags" t-att-href="keep('/shop'+ ('/category/'+slug(category)) if category else None, attrib=0, tags=0)" t-attf-class="btn btn-{{navClass}} d-flex align-items-center py-1 mb-2">
|
|
<small class="mx-auto"><b>Clear Filters</b></small>
|
|
<i class="oi oi-close"/>
|
|
</a>
|
|
<t t-foreach="attributes" t-as="a">
|
|
<t t-cache="a,attrib_set">
|
|
<div class="accordion-item nav-item mb-1 border-0" t-if="a.value_ids and len(a.value_ids) > 1">
|
|
<h6 class="mb-3">
|
|
<b class="o_products_attributes_title d-none d-lg-block" t-field="a.name"/>
|
|
</h6>
|
|
<div t-attf-id="o_products_attributes_{{a.id}}" class="">
|
|
<t t-if="a.display_type == 'select'">
|
|
<select class="form-select css_attribute_select mb-2" name="attrib">
|
|
<option value="" selected="true">-</option>
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<option t-att-value="'%s-%s' % (a.id,v.id)" t-esc="v.name" t-att-selected="v.id in attrib_set" />
|
|
</t>
|
|
</select>
|
|
</t>
|
|
<div t-elif="a.display_type == 'color'" class="mb-3">
|
|
<t t-call="website_sale.o_wsale_offcanvas_color_attribute"/>
|
|
</div>
|
|
<div t-elif="a.display_type in ('radio', 'pills', 'multi')" class="flex-column mb-3">
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<div class="form-check mb-1">
|
|
<input type="checkbox"
|
|
name="attrib"
|
|
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="'checked' if v.id in attrib_set else None"/>
|
|
<label class="form-check-label fw-normal" t-att-for="'%s-%s' % (a.id,v.id)" t-field="v.name"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<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>
|
|
</form>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="filter_products_price" name="Filter by Prices" active="False">
|
|
<t t-set="isDisabled" t-value="available_min_price == available_max_price"/>
|
|
<div id="o_wsale_price_range_option"
|
|
t-attf-class="position-relative {{_classes}} {{isDisabled and 'opacity-75 pe-none user-select-none'}}">
|
|
<label t-attf-class="m-0 h6 o_products_attributes_title {{_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-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>
|
|
</template>
|
|
|
|
<template id="filter_products_tags" name="Filter by Tags" active="True">
|
|
<div t-if="all_tags">
|
|
<h6 class="mb-3">
|
|
<b>Tags</b>
|
|
</h6>
|
|
<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>
|
|
</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>
|
|
|
|
<!-- /shop/product page -->
|
|
<template id="base_unit_price" name="Product Base unit price">
|
|
(<span class="o_base_unit_price" t-esc="combination_info['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="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-cache="pricelist,product,fiscal_position">
|
|
<!-- TODO drop _get_first_possible_combination here -->
|
|
<t t-set="combination" t-value="product._get_first_possible_combination()"/>
|
|
<t t-set="combination_info" t-value="product._get_combination_info(combination, add_qty=add_qty)"/>
|
|
<t t-set="product_variant" t-value="product.env['product.product'].browse(combination_info['product_id'])"/>
|
|
|
|
<t t-call="website.layout">
|
|
<t t-set="additional_title" t-value="product.name" />
|
|
<div itemscope="itemscope" itemtype="http://schema.org/Product" 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="DROP BUILDING BLOCKS HERE TO MAKE THEM AVAILABLE ACROSS ALL PRODUCTS"/>
|
|
<section id="product_detail"
|
|
t-attf-class="container py-4 oe_website_sale #{'discount' if combination_info['has_discounted_price'] else ''}"
|
|
t-att-data-view-track="view_track and '1' or '0'"
|
|
t-att-data-product-tracking-info="'product_tracking_info' in combination_info and json.dumps(combination_info['product_tracking_info'])"
|
|
>
|
|
<div class="row align-items-center">
|
|
<div class="col-lg-6 d-flex align-items-center">
|
|
<div class="d-flex justify-content-between w-100">
|
|
<t t-if="is_view_active('website_sale.search')" t-call="website_sale.search">
|
|
<t t-set="search" t-value="False"/>
|
|
<t t-set="_form_classes" t-valuef="mb-2 mb-lg-0"/>
|
|
<t t-set="_classes" t-value="'me-sm-2'"/>
|
|
</t>
|
|
<t t-set="website_sale_pricelists" t-value="website.get_pricelist_available(show_visible=True)" />
|
|
<t t-set="hasPricelistDropdown" t-value="website_sale_pricelists and len(website_sale_pricelists)>1"/>
|
|
<t t-call="website_sale.pricelist_list">
|
|
<t t-set="_classes" t-valuef="d-lg-inline ms-2"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6 d-flex align-items-center">
|
|
<ol class="breadcrumb p-0 mb-2 m-lg-0">
|
|
<li class="breadcrumb-item o_not_editable">
|
|
<a t-att-href="keep(category=0)">All Products</a>
|
|
</li>
|
|
<li t-nocache="The category does not have to be cached, as the product can be accessed via different paths."
|
|
t-if="category" class="breadcrumb-item">
|
|
<a t-att-href="keep('/shop/category/%s' % slug(category), category=0)" t-field="category.name" />
|
|
</li>
|
|
<li class="breadcrumb-item active">
|
|
<span t-field="product.name" />
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
<div class="row" id="product_detail_main" data-name="Product Page"
|
|
t-att-data-image_width="website.product_page_image_width"
|
|
t-att-data-image_layout="website.product_page_image_layout">
|
|
<t t-set="image_cols" t-value="website._get_product_page_proportions()"/>
|
|
<div t-attf-class="col-lg-#{image_cols[0]} mt-lg-4 o_wsale_product_images position-relative" t-if="website.product_page_image_width != 'none'">
|
|
<t t-call="website_sale.shop_product_images"/>
|
|
</div>
|
|
<div t-attf-class="col-lg-#{image_cols[1]} mt-md-4" id="product_details">
|
|
<t t-set="base_url" t-value="website.get_base_url()"/>
|
|
<!-- TODO: remove next line in master. Stable fix not to break custos. -->
|
|
<t t-if="False" t-set="base_url" t-value="product.get_base_url()"/>
|
|
<h1 itemprop="name" t-field="product.name">Product Name</h1>
|
|
<span itemprop="url" style="display:none;" t-esc="base_url + product.website_url"/>
|
|
<span itemprop="image" style="display:none;" t-esc="base_url + website.image_url(product, 'image_1920')" />
|
|
<t t-if="is_view_active('website_sale.product_comment')">
|
|
<a href="#o_product_page_reviews" class="o_product_page_reviews_link text-decoration-none">
|
|
<t t-call="portal_rating.rating_widget_stars_static">
|
|
<t t-set="rating_avg" t-value="product.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>
|
|
<p t-field="product.description_sale" class="text-muted my-2" placeholder="A short description that will also appear on documents." />
|
|
<div t-field="product.description_ecommerce" class="oe_structure"
|
|
placeholder="A detailed, formatted description to promote your product on this page. Use '/' to discover more features."/>
|
|
<form t-if="product._is_add_to_cart_possible()" action="/shop/cart/update" method="POST">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
|
<div class="js_product js_main_product mb-3">
|
|
<div>
|
|
<t t-call="website_sale.product_price"/>
|
|
<small t-if="'base_unit_price' in combination_info"
|
|
class="ms-1 text-muted o_base_unit_price_wrapper d-none" groups="website_sale.group_show_uom_price">
|
|
<t t-call='website_sale.base_unit_price'/>
|
|
</small>
|
|
</div>
|
|
<t t-placeholder="select">
|
|
<input type="hidden" class="product_id" name="product_id" t-att-value="product_variant.id" />
|
|
<input type="hidden" class="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]" />
|
|
<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>
|
|
<p t-if="True" class="css_not_available_msg alert alert-warning">This combination does not exist.</p>
|
|
<div id="o_wsale_cta_wrapper" class="d-flex flex-wrap align-items-center">
|
|
<t t-set="hasQuantities" t-value="false"/>
|
|
<t t-set="hasBuyNow" t-value="false"/>
|
|
<!-- TODO: remove line below in master -->
|
|
<t t-set="ctaSizeBig" t-value="not hasQuantities or not hasBuyNow"/>
|
|
|
|
<div id="add_to_cart_wrap" t-attf-class="{{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-flex'}} align-items-center mb-2 me-auto">
|
|
<a data-animation-selector=".o_wsale_product_images" role="button" id="add_to_cart" t-attf-class="btn btn-primary js_check_product a-submit flex-grow-1" href="#">
|
|
<i class="fa fa-shopping-cart me-2"/>
|
|
Add to cart
|
|
</a>
|
|
</div>
|
|
<div id="product_option_block" class="d-flex flex-wrap w-100"/>
|
|
</div>
|
|
<div id="contact_us_wrapper"
|
|
t-attf-class="{{'d-flex' if combination_info['prevent_zero_price_sale'] else 'd-none'}} oe_structure oe_structure_solo #{_div_classes}">
|
|
<section class="s_text_block" data-snippet="s_text_block" data-name="Text">
|
|
<div class="container">
|
|
<a t-att-href="website.contact_us_button_url"
|
|
class="btn btn-primary btn_cta">Contact Us
|
|
</a>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<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>
|
|
</div>
|
|
</form>
|
|
<p t-elif="not product.active" class="alert alert-warning">This product is no longer available.</p>
|
|
<p t-else="" class="alert alert-warning">This product has no valid combination.</p>
|
|
<div id="product_attributes_simple">
|
|
<t t-set="single_value_attributes" t-value="product.valid_product_template_attribute_line_ids._prepare_single_value_for_display()"/>
|
|
<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="my-2" t-if="product_documents">
|
|
<h5>Documents</h5>
|
|
<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'"/>
|
|
<div>
|
|
<a t-att-href="'/shop/' + slug(product) + '/document/' + str(document_sudo.id)" t-att-target="target">
|
|
<i t-att-class="'fa ' + icon"/>
|
|
<t t-out="attachment_sudo.name"/>
|
|
</a>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<div id="o_product_terms_and_share" class="d-flex justify-content-between flex-column flex-md-row align-items-md-end mb-3">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<div itemprop="description" t-field="product.website_description" class="oe_structure oe_empty mt16" id="product_full_description"/>
|
|
<div class="oe_structure oe_empty oe_structure_not_nearest mt16" id="oe_structure_website_sale_product_2" data-editor-message="DROP BUILDING BLOCKS HERE TO MAKE THEM AVAILABLE ACROSS ALL PRODUCTS"/>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="product_tags" name="Product Tags" active="True">
|
|
<div class="o_product_tags o_field_tags d-flex flex-wrap align-items-center gap-2">
|
|
<t t-foreach="all_product_tags" t-as="tag">
|
|
<t t-if="tag.visible_on_ecommerce">
|
|
<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>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="alternative_products" name="Alternative Products" inherit_id="website_sale.product" active="True">
|
|
<div itemprop="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_borderless_1" 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 o_not_editable">
|
|
<div class="css_non_editable_mode_hidden">
|
|
<div class="missing_option_warning alert alert-info rounded-0 fade show d-none d-print-none o_default_snippet_text">
|
|
Your Dynamic Snippet will be displayed here...
|
|
This message is displayed because youy did not provide both a filter and a template to use.
|
|
</div>
|
|
</div>
|
|
<div class="dynamic_snippet_template"></div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="product_custom_text" inherit_id="website_sale.product" customize_show="True" active="True" name="Terms and Conditions" priority="21">
|
|
<xpath expr="//div[@id='o_product_terms_and_share']" position="inside">
|
|
<p class="text-muted mb-0">
|
|
<a href="/terms" class="text-muted"><u>Terms and Conditions</u></a><br/>
|
|
30-day money-back guarantee<br/>
|
|
Shipping: 2-3 Business Days
|
|
</p>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="product_share_buttons" inherit_id="website_sale.product" active="True" name="Share Buttons" priority="22">
|
|
<xpath expr="//div[@id='o_product_terms_and_share']" position="inside">
|
|
<div class="h4 mt-3 mb-0 d-flex justify-content-md-end flex-shrink-0" contenteditable="false">
|
|
<t t-snippet-call="website.s_share">
|
|
<t t-set="_exclude_share_links" t-value="['whatsapp', 'linkedin']"/>
|
|
<t t-set="_no_title" t-value="True"/>
|
|
<t t-set="_classes" t-valuef="text-lg-end"/>
|
|
<t t-set="_link_classes" t-valuef="mx-1 my-0"/>
|
|
</t>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Product options: Zoom -->
|
|
<template inherit_id='website_sale.product' id="product_picture_magnify_hover" name="Automatic Image Zoom">
|
|
<xpath expr='//div[hasclass("o_wsale_product_page")]' position='attributes'>
|
|
<attribute name="data-ecom-zoom-auto">1</attribute>
|
|
<attribute name="class" separator=" " add="ecom-zoomable zoomodoo-next" />
|
|
</xpath>
|
|
</template>
|
|
|
|
<template inherit_id='website_sale.product' id="product_picture_magnify_click" active="False" 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 zoomodoo-next" />
|
|
</xpath>
|
|
</template>
|
|
|
|
<template inherit_id='website_sale.product' id="product_picture_magnify_both" active="False" name="Automatic Image Zoom And On Click">
|
|
<xpath expr='//div[hasclass("o_wsale_product_page")]' position='attributes'>
|
|
<attribute name="data-ecom-zoom-auto">1</attribute>
|
|
<attribute name="data-ecom-zoom-click">1</attribute>
|
|
<attribute name="class" separator=" " add="ecom-zoomable zoomodoo-next" />
|
|
</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" data-anchor='true'>
|
|
<section id="o_product_page_reviews" class="container pt32 pb32" data-anchor='true'>
|
|
<a class="o_product_page_reviews_title d-flex justify-content-between 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">
|
|
<h3 class="mb32">Customer Reviews</h3>
|
|
<i class="fa align-self-start"/>
|
|
</a>
|
|
<div id="o_product_page_reviews_content" class="collapse">
|
|
<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="product_quantity" inherit_id="website_sale.product" name="Select Quantity">
|
|
<xpath expr="//t[@t-set='hasQuantities']" position="attributes">
|
|
<attribute name="t-value" remove="false" add="true" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='add_to_cart_wrap']" position="before">
|
|
<div t-attf-class="css_quantity input-group {{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-flex'}} me-2 mb-2 align-middle" contenteditable="false">
|
|
<a t-attf-href="#" class="btn btn-link js_add_cart_json" aria-label="Remove one" title="Remove one">
|
|
<i class="fa fa-minus"></i>
|
|
</a>
|
|
<input type="text" class="form-control quantity text-center" data-min="1" name="add_qty" t-att-value="add_qty or 1"/>
|
|
<a t-attf-href="#" class="btn btn-link float_left js_add_cart_json" aria-label="Add one" title="Add one">
|
|
<i class="fa fa-plus"></i>
|
|
</a>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="product_buy_now" inherit_id="website_sale.product" active="False" name="Buy Now Button">
|
|
<xpath expr="//t[@t-set='hasBuyNow']" position="attributes">
|
|
<attribute name="t-value" remove="false" add="true" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//a[@id='add_to_cart']" position="after">
|
|
<a role="button" class="btn btn-outline-primary o_we_buy_now ms-1" href="#">
|
|
<i class="fa fa-bolt me-2"/>
|
|
Buy now
|
|
</a>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="website_sale.tax_indication" active="False">
|
|
<span t-if="website.show_line_subtotals_tax_selection == 'tax_excluded'" class="h6 text-muted">
|
|
VAT Excluded
|
|
</span>
|
|
<span t-else="" class="h6 text-muted">
|
|
VAT Included
|
|
</span>
|
|
</template>
|
|
|
|
<template id="product_price">
|
|
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" t-attf-class="product_price mt-2 mb-3 {{'d-none' if combination_info['prevent_zero_price_sale'] else 'd-inline-block'}}">
|
|
<h3 class="css_editable_mode_hidden">
|
|
<span class="oe_price"
|
|
style="white-space: nowrap;"
|
|
t-out="combination_info['price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
<span itemprop="price" style="display:none;" t-out="combination_info['price']"/>
|
|
<span itemprop="priceCurrency" style="display:none;" t-esc="website.currency_id.name"/>
|
|
<span t-attf-class="text-danger oe_default_price ms-1 h5 {{'' if combination_info['has_discounted_price'] and not combination_info['compare_list_price'] else 'd-none'}}"
|
|
style="text-decoration: line-through; white-space: nowrap;"
|
|
t-esc="combination_info['list_price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
itemprop="listPrice"
|
|
/>
|
|
<t t-if="is_view_active('website_sale.tax_indication')" t-call="website_sale.tax_indication"/>
|
|
<del t-if="combination_info['compare_list_price'] and (combination_info['compare_list_price'] > combination_info['price'])">
|
|
<bdi dir="inherit">
|
|
<span t-esc="combination_info['compare_list_price']"
|
|
groups="website_sale.group_product_price_comparison"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
</bdi>
|
|
</del>
|
|
</h3>
|
|
<h3 class="css_non_editable_mode_hidden decimal_precision" 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['compare_list_price'] and (combination_info['compare_list_price'] > combination_info['price'])">
|
|
<bdi dir="inherit">
|
|
<span t-field="product.compare_list_price"
|
|
groups="website_sale.group_product_price_comparison"
|
|
t-options="{'widget': 'monetary', 'display_currency': product.currency_id}"/>
|
|
</bdi>
|
|
</del>
|
|
</h3>
|
|
</div>
|
|
<div id="product_unavailable" t-attf-class="{{'d-flex' if combination_info['prevent_zero_price_sale'] else 'd-none'}}">
|
|
<h3 class="fst-italic" t-field="website.prevent_zero_price_sale_text"/>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="product_variants" inherit_id="website_sale.product" active="False" name="List View of Variants">
|
|
<xpath expr="//t[@t-placeholder='select']" position="replace">
|
|
<!--
|
|
Using this setting with dynamic variants is not supported.
|
|
Indeed the variants that have yet to exist will not show on the
|
|
list and will never be selectable to be created...
|
|
|
|
We also don't use the feature with no_variant because these
|
|
attributes have to be selected manually.
|
|
|
|
Finally we don't use the feature with is_custom values because
|
|
they need to be set by the user.
|
|
-->
|
|
<t t-if="not product.has_dynamic_attributes() and not product._has_no_variant_attributes() and not product._has_is_custom_values()">
|
|
<t t-set="attribute_exclusions" t-value="product._get_attribute_exclusions()"/>
|
|
<t t-set="filtered_sorted_variants" t-value="product._get_possible_variants_sorted()"/>
|
|
<ul class="d-none js_add_cart_variants mb-0" t-att-data-attribute_exclusions="json.dumps(attribute_exclusions)"/>
|
|
<input type="hidden" class="product_template_id" t-att-value="product.id"/>
|
|
<input type="hidden" t-if="len(filtered_sorted_variants) == 1" class="product_id" name="product_id" t-att-value="filtered_sorted_variants[0].id"/>
|
|
<t t-if="len(filtered_sorted_variants) > 1">
|
|
<div class="mb-4">
|
|
<div t-foreach="filtered_sorted_variants" t-as="variant_id" class="form-check mb-1">
|
|
<t t-set="template_combination_info" t-value="product._get_combination_info(only_template=True, add_qty=add_qty)"/>
|
|
<t t-set="combination_info" t-value="variant_id._get_combination_info_variant(add_qty=add_qty)"/>
|
|
<input type="radio"
|
|
name="product_id"
|
|
class="form-check-input product_id js_product_change"
|
|
t-att-checked="'checked' if variant_id_index == 0 else None"
|
|
t-attf-id="radio_variant_#{variant_id.id}"
|
|
t-att-value="variant_id.id"
|
|
t-att-data-price="combination_info['price']"
|
|
t-att-data-combination="variant_id.product_template_attribute_value_ids.ids"/>
|
|
<label t-attf-for="radio_variant_#{variant_id.id}" label-default="label-default" class="form-check-label fw-normal">
|
|
<span t-out="combination_info['display_name']"/>
|
|
<t t-set="diff_price" t-value="website.currency_id.compare_amounts(combination_info['price'], template_combination_info['price'])"/>
|
|
<span t-attf-class="badge rounded-pill text-bg-{{navClass}} border" t-if="diff_price != 0">
|
|
<span class="sign_badge_price_extra" t-out="diff_price > 0 and '+' or '-'"/>
|
|
<span t-out="abs(combination_info['price'] - template_combination_info['price'])"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
class="text-muted fst-italic"/>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<t t-else="">$0</t>
|
|
</xpath>
|
|
</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="show_navigation_button" t-value="False"/>
|
|
<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>
|
|
|
|
<h3 class="mb-4">Extra info</h3>
|
|
<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">
|
|
<div class="s_website_form_field col-12 py-2 mb-0" data-type="char" data-name="Field">
|
|
<div class="s_col_no_resize s_col_no_bgcolor row">
|
|
<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 py-2 mb-0" data-type="text" data-name="Field">
|
|
<div class="s_col_no_resize s_col_no_bgcolor row">
|
|
<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 py-2 mb-0" data-type="binary" data-name="Field">
|
|
<div class="s_col_no_resize s_col_no_bgcolor row">
|
|
<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 class="s_website_form_submit s_website_form_no_submit_option d-flex flex-column flex-lg-row align-items-lg-center pt-4">
|
|
<a role="button"
|
|
name="website_sale_main_button"
|
|
class="s_website_form_send btn btn-primary order-lg-3 w-100 w-lg-auto ms-lg-auto"
|
|
href="/shop/confirm_order">
|
|
Continue checkout
|
|
<i class="fa fa-angle-right ms-2 fw-light"/>
|
|
</a>
|
|
|
|
<div class="position-relative d-flex d-lg-none 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>
|
|
|
|
<a href="/shop/checkout" class="text-center">
|
|
<i class="fa fa-angle-left me-2 fw-light"/>
|
|
Return to shipping
|
|
</a>
|
|
<span id="s_website_form_result"/>
|
|
</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-att-href="line.product_id.website_url">
|
|
<t t-out="0"/>
|
|
</a>
|
|
</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" t-attf-class="text-muted {{div_class}} small">
|
|
<t t-foreach="description_lines" t-as="name_line">
|
|
<span t-if="name_line" class="d-block" 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 alert alert-info">
|
|
Your cart is empty!
|
|
</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> <t t-esc='website_sale_order._get_shop_warning()'/>
|
|
</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-set="show_qty" t-value="is_view_active('website_sale.product_quantity')"/>
|
|
<div t-foreach="website_sale_order.website_order_line"
|
|
t-as="line"
|
|
t-attf-class="o_cart_product d-flex align-items-stretch gap-3 #{line.linked_line_id and 'optional_product info'} #{not line_last and 'border-bottom pb-4'} #{line_index > 0 and 'pt-4'}"
|
|
t-attf-data-product-id="#{line.product_id and line.product_id.id}">
|
|
<t t-if="line.product_id">
|
|
<div style="width: 64px">
|
|
<!--
|
|
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).
|
|
-->
|
|
<img
|
|
t-if="line._is_not_sellable_line() and line.product_id.image_128"
|
|
t-att-src="image_data_uri(line.product_id.image_128)"
|
|
class="o_image_64_max img 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': 'o_image_64_max rounded',
|
|
}"
|
|
/>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<t t-call="website_sale.cart_line_product_link">
|
|
<h6 t-field="line.name_short" class="d-inline align-top h6 fw-bold"/>
|
|
</t>
|
|
<t t-call="website_sale.cart_line_description_following_lines">
|
|
<t t-set="div_class" t-value="''"/>
|
|
</t>
|
|
<div>
|
|
<a href='#'
|
|
class="js_delete_product d-none d-md-inline-block small"
|
|
aria-label="Remove from cart"
|
|
title="Remove from cart">Remove</a>
|
|
<button class="js_delete_product btn btn-light d-inline-block d-md-none"
|
|
title="remove">
|
|
<i class="fa fa-trash-o"/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex flex-column align-items-end">
|
|
<div t-attf-class="css_quantity input-group mb-2"
|
|
name="website_sale_cart_line_quantity">
|
|
<t t-if="not line._is_not_sellable_line()">
|
|
<t t-if="show_qty">
|
|
<a href="#"
|
|
class="js_add_cart_json btn btn-link d-inline-block border-end-0"
|
|
aria-label="Remove one"
|
|
title="Remove one">
|
|
<i class="position-relative z-index-1 fa fa-minus"/>
|
|
</a>
|
|
<input type="text"
|
|
class="js_quantity quantity form-control border-start-0 border-end-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="js_add_cart_json d-inline-block float_left btn btn-link border-start-0"
|
|
aria-label="Add one"
|
|
title="Add one">
|
|
<i class="fa fa-plus position-relative z-index-1"/>
|
|
</a>
|
|
</t>
|
|
<t t-else="">
|
|
<input type="hidden"
|
|
class="js_quantity form-control quantity"
|
|
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>
|
|
<t t-else="">
|
|
<span class="w-100 text-muted" t-esc="int(line.product_uom_qty)"/>
|
|
<input type="hidden"
|
|
class="js_quantity quantity form-control"
|
|
t-att-data-line-id="line.id"
|
|
t-att-data-product-id="line.product_id.id"
|
|
t-att-value="line._get_displayed_quantity()"/>
|
|
</t>
|
|
</div>
|
|
<div class="mb-0 h6 fw-bold text-end" name="website_sale_cart_line_price">
|
|
<t t-if="line.discount">
|
|
<del t-attf-class="#{'text-danger mr8'}"
|
|
style="white-space: nowrap;"
|
|
t-out="line._get_displayed_unit_price() * line.product_uom_qty"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
</t>
|
|
<t t-if="website.show_line_subtotals_tax_selection == 'tax_excluded'"
|
|
t-set='product_price'
|
|
t-value='line.price_subtotal'/>
|
|
<t t-else=""
|
|
t-set='product_price'
|
|
t-value='line.price_total'/>
|
|
<span t-out="product_price" style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
<small t-if="not line._is_not_sellable_line() and line.product_id.base_unit_price"
|
|
class="cart_product_base_unit_price d-block text-muted"
|
|
groups="website_sale.group_show_uom_price">
|
|
<t t-call='website_sale.base_unit_price'>
|
|
<t t-set='product' t-value='line.product_id'/>
|
|
<t t-set='combination_info'
|
|
t-value="{'base_unit_price': product._get_base_unit_price(product_price/line.product_uom_qty)}"/>
|
|
</t>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</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 class="col">
|
|
<h3 class="mb-4">Order overview</h3>
|
|
<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 t-attf-href="/shop/cart/?access_token=#{access_token}&revive=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 t-attf-href="/shop/cart/?access_token=#{access_token}&revive=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>
|
|
|
|
<!-- 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="mt32 mb-3" t-if="suggested_products">Suggested accessories</h5>
|
|
<div t-if="suggested_products"
|
|
id="suggested_products"
|
|
class="d-flex flex-column align-items-stretch mb32">
|
|
<div t-foreach="suggested_products"
|
|
t-as="product"
|
|
t-attf-class="d-flex gap-3 #{not product_last and 'border-bottom pb-4'} #{product_index > 0 and 'pt-4'}"
|
|
t-att-data-publish="product.website_published and 'on' or 'off'">
|
|
<div style="width: 64px">
|
|
<a t-att-href="product.website_url">
|
|
<span t-field="product.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'o_image_64_max rounded'}"/>
|
|
</a>
|
|
</div>
|
|
<div class="o_cart_suggested_product_name flex-grow-1">
|
|
<div>
|
|
<a t-att-href="product.website_url">
|
|
<strong t-out="product.with_context(display_default_code=False).display_name"/>
|
|
</a>
|
|
</div>
|
|
<div class="d-none d-md-block text-muted" t-field="product.description_sale"/>
|
|
</div>
|
|
<div class="d-flex flex-column align-items-end">
|
|
<input class="js_quantity" name="product_id" t-att-data-product-id="product.id" type="hidden"/>
|
|
<a t-if="product._website_show_quick_add()"
|
|
role="button"
|
|
class="js_add_suggested_products btn btn-md btn-outline-primary text-nowrap">
|
|
<span class="d-md-none fa fa-shopping-cart"/>
|
|
<span class="d-none d-md-inline">Add to cart</span>
|
|
</a>
|
|
<div class="mb-0 h-6 fw-bold text-end d-flex"
|
|
name="website_sale_suggested_product_price">
|
|
<t t-set="combination_info"
|
|
t-value="product._get_combination_info_variant()"/>
|
|
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}"
|
|
t-esc="combination_info['list_price']"
|
|
t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"
|
|
style="white-space: nowrap;"/>
|
|
<span t-esc="combination_info['price']"
|
|
t-options="{'widget': 'monetary','display_currency': website.currency_id}"
|
|
style="white-space: nowrap;"/>
|
|
</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 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()" t-nocache="The csrf token must always be up to date."/>
|
|
<div class="input-group w-100 my-2">
|
|
<input name="promo" class="form-control" type="text" placeholder="Discount code..." t-att-value="website_sale_order.pricelist_id.code or None"/>
|
|
<a href="#" role="button" class="btn btn-secondary a-submit ps-2">Apply</a>
|
|
</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:
|
|
- _cta_classes: CSS classes to append on the primary navigation button; default `None`.
|
|
- _form_send_navigation: Whether the primary button serves as a submit button for a
|
|
form; default `None`.
|
|
- hide_payment_button: Whether the payment button should be hidden; default: False.
|
|
-->
|
|
<!-- Checkout context:
|
|
- website_sale_order: The current order.
|
|
- xmlid: The id of the xml templated rendered by the controller.
|
|
-->
|
|
<t t-set="step_specific_values" t-value="website._get_checkout_steps(xmlid)"/>
|
|
<div t-attf-class="#{_container_classes} d-flex #{_form_send_navigation and 'flex-column flex-lg-row align-items-lg-center' or 'flex-column'} mb-5 mb-lg-0 pt-4">
|
|
<t t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<t t-if="xmlid == 'website_sale.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-nocache="The csrf token must always be up to date."/>
|
|
<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="#{_cta_classes} btn btn-primary #{not website_sale_order._is_cart_ready() and 'disabled'} #{_form_send_navigation and 'order-lg-3 w-100 w-lg-auto ms-lg-auto' or 'w-100'}"
|
|
t-att-href="step_specific_values['main_button_href']">
|
|
<t t-out="step_specific_values['main_button']"/>
|
|
<i class="fa fa-angle-right ms-2 fw-light"/>
|
|
</a>
|
|
</t>
|
|
</t>
|
|
<div t-if="not hide_payment_button" t-attf-class="position-relative #{_form_send_navigation and 'd-flex d-lg-none' or 'd-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>
|
|
<a t-att-href="step_specific_values['back_button_href']" class="text-center">
|
|
<i class="fa fa-angle-left me-2 fw-light"/>
|
|
<t t-out="step_specific_values['back_button']"/>
|
|
</a>
|
|
</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 class="row">
|
|
<div class="col-lg-12">
|
|
<h3 class="mb-4">Address</h3>
|
|
<h4>Billing</h4>
|
|
</div>
|
|
</div>
|
|
<t t-call="website_sale.row_addresses">
|
|
<t t-set="order" t-value="order"/>
|
|
<t t-set="is_invoice" t-value="True"/>
|
|
<t t-set="addresses" t-value="billings"/>
|
|
<t t-set="selected_address" t-value="order.partner_invoice_id"/>
|
|
</t>
|
|
<t t-if="not only_services" groups="account.group_delivery_invoice_address">
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<h4>Shipping</h4>
|
|
</div>
|
|
</div>
|
|
<t t-call="website_sale.row_addresses">
|
|
<t t-set="order" t-value="order"/>
|
|
<t t-set="is_invoice" t-value="False"/>
|
|
<t t-set="addresses" t-value="shippings"/>
|
|
<t t-set="selected_address" t-value="order.partner_shipping_id"/>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="row_addresses">
|
|
<div t-attf-class="{{'all_billing' if is_invoice else 'all_shipping'}} row row-cols-md-2 row-cols-lg-3 g-3 flex-nowrap flex-md-wrap mb32">
|
|
<div t-foreach="addresses" t-as="addr"
|
|
class="one_kanban col-md">
|
|
<t t-call="website_sale.address_kanban">
|
|
<t t-set="contact" t-value="addr"/>
|
|
<t t-set="selected" t-value="len(addresses) == 1 or addr == selected_address"/>
|
|
</t>
|
|
</div>
|
|
<div t-if="not is_invoice or not order.website_id.is_public_user()" class="one_kanban col-md">
|
|
<!-- We do not allow public users to have multiple billing addresses -->
|
|
<t t-if="is_invoice">
|
|
<t t-set="new_address_href" t-valuef="/shop/address?mode=billing"/>
|
|
</t>
|
|
<t t-else="">
|
|
<t t-set="new_address_href" t-valuef="/shop/address?mode=shipping"/>
|
|
</t>
|
|
<a role="button" t-att-href="new_address_href" class="o_wsale_add_address d-flex align-items-center justify-content-center h-100 px-4 border rounded mx-auto no-decoration">
|
|
<i class="fa fa-plus me-md-2"/><span class="d-none d-md-inline">Add address</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Card view of addresses. Called in `website_sale.checkout`. -->
|
|
<template id="address_kanban" name="Kanban address">
|
|
<t t-set="mode" t-value="is_invoice and 'billing' or 'shipping'"/>
|
|
<form action="/shop/cart/update_address" method="POST" class="d-none">
|
|
<input type="hidden"
|
|
name="csrf_token"
|
|
t-att-value="request.csrf_token()"
|
|
t-nocache="The csrf token must always be up to date."/>
|
|
<input type="hidden" name="partner_id" t-att-value="contact.id"/>
|
|
<input type="hidden" name="mode" t-att-value="mode"/>
|
|
<input type="submit"/>
|
|
</form>
|
|
<div t-attf-class="card position-relative h-100 #{selected and 'bg-primary border border-primary' or is_invoice and 'js_change_billing' or 'js_change_shipping'}">
|
|
<div class="card-body d-flex flex-column align-items-start">
|
|
<t t-esc="contact" t-options="dict(widget='contact', fields=['name', 'address'], no_marker=True)"/>
|
|
<t t-if="contact._can_be_edited_by_current_customer(website_sale_order, mode)">
|
|
<t t-set="new_address_href" t-value="'/shop/address?mode=' + mode"/>
|
|
<a
|
|
t-att-href="new_address_href + '&partner_id=' + str(contact.id)"
|
|
class="js_edit_address btn btn-link p-0 mt-auto"
|
|
role="button"
|
|
title="Edit this address"
|
|
aria-label="Edit this address">
|
|
<i class="fa fa-pencil me-1"/>Edit
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- /shop/address route -->
|
|
<template id="address" name="Address Management">
|
|
<t t-set="no_footer" t-value="1"/>
|
|
<t t-call="website.layout">
|
|
<div id="wrap">
|
|
<div class="oe_website_sale o_wsale_address_fill container py-2">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<t t-call="website_sale.wizard_checkout"/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="oe_cart col-12 col-lg-8">
|
|
<div>
|
|
<t t-set="address_mode" t-value="mode[1]"/>
|
|
<t t-if="is_public_order">
|
|
<h3 class="mb-3">
|
|
<span t-if="account_on_checkout != 'mandatory'">Fill in your address</span>
|
|
<small class="text-muted" t-if="account_on_checkout == 'optional'"> or </small>
|
|
<a t-if="account_on_checkout != 'disabled'" role="button" href='/web/login?redirect=/shop/checkout' style="margin-top: -11px"> Sign in</a>
|
|
</h3>
|
|
</t>
|
|
<t t-elif="address_mode == 'billing'">
|
|
<h3 class="mb-3">Billing address</h3>
|
|
</t>
|
|
<t t-else="">
|
|
<h3 class="mb-3">Shipping address</h3>
|
|
</t>
|
|
<t t-if="partner_id == website_sale_order.partner_shipping_id.id == website_sale_order.partner_invoice_id.id">
|
|
<div class="alert alert-warning" role="alert" t-if="not only_services" groups="account.group_delivery_invoice_address">
|
|
<h4 class="alert-heading">Be aware!</h4>
|
|
<p>
|
|
You are editing your <b>billing and shipping</b> addresses at the same time!<br/>
|
|
If you want to modify your shipping address, create a <a href='/shop/address'>new address</a>.
|
|
</p>
|
|
</div>
|
|
</t>
|
|
<t t-if="error" t-foreach="error.get('error_message', [])" t-as="err">
|
|
<h5 class="text-danger" t-esc="err" />
|
|
</t>
|
|
<form t-if="account_on_checkout != 'mandatory' or not is_public_user" action="/shop/address" method="post" class="checkout_autoformat">
|
|
<div class="row">
|
|
<div t-attf-class="#{error.get('name') and 'o_has_error'} div_name col-lg-12 mb-2">
|
|
<label class="col-form-label" for="name">Full name</label>
|
|
<input type="text" name="name" t-attf-class="form-control #{error.get('name') and 'is-invalid' or ''}" t-att-value="'name' in checkout and checkout['name']" />
|
|
</div>
|
|
<div class="w-100"/>
|
|
<div t-attf-class="#{error.get('email') and 'o_has_error'} col-lg-6 mb-2" id="div_email">
|
|
<label t-attf-class="col-form-label #{mode[1] == 'shipping' and 'label-optional' or ''}" for="email">Email</label>
|
|
<input type="email" name="email" t-attf-class="form-control #{error.get('email') and 'is-invalid' or ''}" t-att-value="'email' in checkout and checkout['email']" />
|
|
</div>
|
|
<div t-attf-class="#{error.get('phone') and 'o_has_error'} col-lg-6 mb-2" id="div_phone">
|
|
<label class="col-form-label" for="phone">Phone</label>
|
|
<input type="tel" name="phone" t-attf-class="form-control #{error.get('phone') and 'is-invalid' or ''}" t-att-value="'phone' in checkout and checkout['phone']" />
|
|
</div>
|
|
<t t-if="website._display_partner_b2b_fields()">
|
|
<div class="w-100"/>
|
|
<t t-set='vat_warning' t-value="'vat' in checkout and checkout['vat'] and not can_edit_vat" />
|
|
<t t-if="(mode == ('new', 'billing') and is_public_order
|
|
or mode == ('edit', 'billing') and partner_id == website_sale_order.partner_id.id)
|
|
and (can_edit_vat or 'vat' in checkout and checkout['vat'])"
|
|
>
|
|
<div t-attf-class="#{error.get('company_name') and 'o_has_error'} col-lg-6 mb-2">
|
|
<label class="col-form-label fw-normal label-optional" for="company_name">Company Name</label>
|
|
<input type="text" name="company_name" t-attf-class="form-control #{error.get('company_name') and 'is-invalid' or ''}" t-att-value="'commercial_company_name' in checkout and checkout['commercial_company_name'] or 'company_name' in checkout and checkout['company_name']" t-att-readonly="'1' if vat_warning else None" />
|
|
<small t-if="vat_warning" class="form-text text-muted d-block d-lg-none">Changing company name is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.</small>
|
|
</div>
|
|
<div t-attf-class="#{error.get('vat') and 'o_has_error'} div_vat col-lg-6 mb-2">
|
|
<label class="col-form-label fw-normal label-optional" for="vat">VAT</label>
|
|
<input type="text" name="vat" t-attf-class="form-control #{error.get('vat') and 'is-invalid' or ''}" t-att-value="'vat' in checkout and checkout['vat']" t-att-readonly="'1' if vat_warning else None"/>
|
|
<small t-if="vat_warning" class="form-text text-muted d-block d-lg-none">Changing VAT number is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.</small>
|
|
</div>
|
|
<div t-if="vat_warning" class="col-12 d-none d-lg-block mb-1">
|
|
<small class="form-text text-muted">Changing company name or VAT number is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.</small>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<div t-attf-class="#{error.get('street') and 'o_has_error'} div_street col-lg-12 mb-2">
|
|
<label class="col-form-label" for="street">Street and Number</label>
|
|
<input type="text" name="street" t-attf-class="form-control #{error.get('street') and 'is-invalid' or ''}" t-att-value="'street' in checkout and checkout['street']" />
|
|
</div>
|
|
<div t-attf-class="mb-2 #{error.get('street2') and 'o_has_error' or ''} col-lg-12 div_street2">
|
|
<label class="col-form-label label-optional" for="street2">Apartment, suite, etc.</label>
|
|
<input type="text" name="street2" t-attf-class="form-control #{error.get('street2') and 'is-invalid' or ''}" t-att-value="'street2' in checkout and checkout['street2']" />
|
|
</div>
|
|
<div class="w-100"/>
|
|
<t t-set='zip_city' t-value='country and [x for x in country.get_address_fields() if x in ["zip", "city"]] or ["city", "zip"]'/>
|
|
<t t-if="'zip' in zip_city and zip_city.index('zip') < zip_city.index('city')">
|
|
<div t-attf-class="#{error.get('zip') and 'o_has_error'} div_zip col-md-4 mb-2">
|
|
<label class="col-form-label label-optional" for="zip">Zip Code</label>
|
|
<input type="text" name="zip" t-attf-class="form-control #{error.get('zip') and 'is-invalid' or ''}" t-att-value="'zip' in checkout and checkout['zip']" />
|
|
</div>
|
|
</t>
|
|
<div t-attf-class="#{error.get('city') and 'o_has_error' or ''} div_city col-md-8 mb-2">
|
|
<label class="col-form-label" for="city">City</label>
|
|
<input type="text" name="city" t-attf-class="form-control #{error.get('city') and 'is-invalid' or ''}" t-att-value="'city' in checkout and checkout['city']" />
|
|
</div>
|
|
<t t-if="'zip' in zip_city and zip_city.index('zip') > zip_city.index('city')">
|
|
<div t-attf-class="#{error.get('zip') and 'o_has_error'} div_zip col-md-4 mb-2">
|
|
<label class="col-form-label label-optional" for="zip">Zip Code</label>
|
|
<input type="text" name="zip" t-attf-class="form-control #{error.get('zip') and 'is-invalid' or ''}" t-att-value="'zip' in checkout and checkout['zip']" />
|
|
</div>
|
|
</t>
|
|
<div class="w-100"/>
|
|
<div t-attf-class="#{error.get('country_id') and 'o_has_error'} div_country col-lg-6 mb-2">
|
|
<label class="col-form-label" for="country_id">Country</label>
|
|
<select id="country_id" name="country_id" t-attf-class="form-select #{error.get('country_id') and 'is-invalid' or ''}" t-att-mode="mode[1]">
|
|
<option value="">Country...</option>
|
|
<t t-foreach="countries" t-as="c">
|
|
<option t-att-value="c.id" t-att-selected="c.id == (country and country.id or -1)">
|
|
<t t-esc="c.name" />
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div t-attf-class="#{error.get('state_id') and 'o_has_error'} div_state col-lg-6 mb-2" t-att-style="(not country or not country.state_ids) and 'display: none'">
|
|
<label class="col-form-label" for="state_id">State / Province</label>
|
|
<select name="state_id" t-attf-class="form-select #{error.get('state_id') and 'is-invalid' or ''}" data-init="1">
|
|
<option value="">State / Province...</option>
|
|
<t t-foreach="country_states" t-as="s">
|
|
<option t-att-value="s.id" t-att-selected="s.id == ('state_id' in checkout and country and checkout['state_id'] != '' and int(checkout['state_id']))">
|
|
<t t-esc="s.name" />
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="w-100"/>
|
|
<t t-if="mode == ('new', 'billing') and not only_services">
|
|
<div class="col-lg-12">
|
|
<div class="form-check form-switch mt-2 mb-3">
|
|
<label>
|
|
<input
|
|
type="checkbox"
|
|
id="shipping_use_same"
|
|
class="form-check-input mr8"
|
|
name="use_same"
|
|
value="1"
|
|
t-att-checked="use_same"/>Ship to the same address
|
|
<span
|
|
t-if="is_public_user"
|
|
class="form-check-label ship_to_other text-muted"
|
|
style="display: none">
|
|
&nbsp;(<i>Your shipping address will be requested later)</i>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<t t-else="">
|
|
<input type="hidden" name="use_same" t-att-value="partner_id == website_sale_order.partner_shipping_id.id == website_sale_order.partner_invoice_id.id"/>
|
|
</t>
|
|
</div>
|
|
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
|
<input type="hidden" name="submitted" value="1" />
|
|
<input type="hidden" name="partner_id" t-att-value="partner_id or '0'" />
|
|
<input type="hidden" name="mode" t-att-value="mode[1]"/>
|
|
<input type="hidden" name="callback" t-att-value="callback" />
|
|
|
|
<!-- Example -->
|
|
<input type="hidden" name="field_required" t-att-value="'name,street'" />
|
|
|
|
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between mt32 mb32">
|
|
<a role="button" t-att-href="mode == ('new', 'billing') and '/shop/cart' or '/shop/checkout'" class="btn btn-outline-secondary w-100 w-md-auto order-md-1 order-3">
|
|
<i class="fw-light fa fa-angle-left me-2"/>Discard
|
|
</a>
|
|
<div class="position-relative w-100 d-flex d-md-none justify-content-center align-items-center order-2 my-2 opacity-75">
|
|
<hr class="w-100"/>
|
|
<span class="px-3">or</span>
|
|
<hr class="w-100"/>
|
|
</div>
|
|
<a role="button" href="#" class="a-submit a-submit-disable a-submit-loading btn btn-primary w-100 w-md-auto order-1 order-md-3">
|
|
<t t-if="mode == ('new', 'billing')">
|
|
Continue checkout
|
|
</t>
|
|
<t t-else="">
|
|
Save address
|
|
</t>
|
|
<i class="fw-light fa fa-angle-right ms-2"/>
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Deactivatable through the website editor. -->
|
|
<template id="address_b2b" inherit_id="address" name="Show b2b fields" />
|
|
|
|
<!-- Called in `website_sale.payment` and `website_sale.confirmation`. -->
|
|
<template id="address_on_payment" name="Address on payment">
|
|
<div class="card o_not_editable">
|
|
<div class="card-body" id="shipping_and_billing">
|
|
<a t-if="not disable_edit" class="float-end no-decoration" href="/shop/checkout"><i class="fa fa-pencil me-1"/>Edit</a>
|
|
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_invoice_id or only_services)" />
|
|
<t t-set="order_address" t-value="order.with_context(show_address=True)"/>
|
|
<div>
|
|
<b>Billing<t t-if="same_shipping and not only_services"> & Shipping</t>: </b>
|
|
<span
|
|
t-esc="', '.join(order_address.partner_invoice_id.display_name.split('\n')[1:])"
|
|
class="address-inline lh-sm o_address_font_sm"/>
|
|
</div>
|
|
<div t-if="not same_shipping and not only_services" groups="account.group_delivery_invoice_address">
|
|
<b>Shipping: </b>
|
|
<span
|
|
id="shipping_on_payment_details"
|
|
t-esc="', '.join(order_address.partner_shipping_id.display_name.split('\n')[1:])"
|
|
class="address-inline lh-sm o_address_font_sm"/>
|
|
</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-esc="error[0]" />
|
|
</h4>
|
|
<t t-esc="error[1]" />
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<h3 class="mb-4">Confirm order</h3>
|
|
<div id="address_on_payment" class="mb-4">
|
|
<t t-call="website_sale.address_on_payment"/>
|
|
</div>
|
|
<div class="oe_structure clearfix mt-3" id="oe_structure_website_sale_payment_1"/>
|
|
|
|
<t t-if="not errors and website_sale_order.amount_total" name="website_sale_non_free_cart">
|
|
<div t-if="payment_methods_sudo or tokens_sudo"
|
|
id="payment_method"
|
|
class="o_not_editable mt-4">
|
|
<t t-call="payment.form"/>
|
|
</div>
|
|
<div t-else="" class="alert alert-warning mt-4">
|
|
<t t-set="hide_payment_button" t-value="True"/>
|
|
<strong>No suitable payment option could be found.</strong><br/>
|
|
<div t-if="not request.env.is_admin()">
|
|
If you believe that it is an error, please contact the website administrator.
|
|
</div>
|
|
<div class="mt-2" groups="base.group_system">
|
|
<a t-if="request.env.company.country_id.is_stripe_supported_country"
|
|
t-attf-href="/web#action=#{action_activate_stripe_id}"
|
|
role="button"
|
|
class="btn btn-primary"
|
|
t-out="'ACTIVATE STRIPE'"
|
|
/>
|
|
<div t-else=""
|
|
class="d-inline"
|
|
title="Stripe Connect is not available in your country, please use another payment provider."
|
|
>
|
|
<button t-out="'ACTIVATE STRIPE'" class="btn btn-primary" disabled="true"/>
|
|
</div>
|
|
<a role="button"
|
|
class="btn-link alert-warning ps-2"
|
|
t-attf-href="/web#action=#{payment_action_id}"
|
|
>
|
|
<strong><i class="oi oi-arrow-right"></i> View alternatives</strong>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</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`.
|
|
- 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="show_wizard_checkout" t-value="True if show_wizard_checkout is None else show_wizard_checkout"/>
|
|
<div id="wrap">
|
|
<div class="oe_website_sale o_website_sale_checkout container py-2">
|
|
<div t-attf-class="row #{show_navigation_button and 'position-relative'} #{not show_wizard_checkout and 'mt32'} mb32">
|
|
<div t-if="show_wizard_checkout" class="col-12">
|
|
<t t-call="website_sale.wizard_checkout"/>
|
|
</div>
|
|
<div t-if="show_shorter_cart_summary"
|
|
class="offset-xl-1 col-lg-5 col-xl-4 order-2"
|
|
id="o_cart_summary">
|
|
<div class="o_total_card card sticky-lg-top"
|
|
t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<div class="card-body p-0 p-lg-4">
|
|
<t t-call="website_sale.total"/>
|
|
<t
|
|
t-if="website.account_on_checkout != 'mandatory' or
|
|
not website.is_public_user()"
|
|
t-call="payment.express_checkout"
|
|
/>
|
|
<t t-call="website_sale.navigation_buttons"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div t-else=""
|
|
class="o_wsale_accordion accordion sticky-lg-top offset-xl-1 col-12 col-lg-5 col-xl-4 order-lg-2 rounded"
|
|
id="o_wsale_total_accordion">
|
|
<div class="o_total_card sticky-lg-top">
|
|
<div id="o_wsale_total_accordion_item" class="accordion-item p-lg-4 border-0">
|
|
<div class="accordion-header d-block align-items-center mb-4">
|
|
<button class="accordion-button px-0 collapsed"
|
|
data-bs-toggle="collapse"
|
|
data-bs-target="#o_wsale_accordion_item"
|
|
aria-expanded="false"
|
|
aria-controls="o_wsale_accordion_item">
|
|
<div class="d-flex flex-wrap">
|
|
<b class="w-100">Order summary</b>
|
|
<span t-out="str(website_sale_order.cart_quantity)"/>
|
|
&nbsp;item(s)&nbsp;-&nbsp;
|
|
<span id="amount_total_summary"
|
|
class="monetary_field ms-1"
|
|
t-field="website_sale_order.amount_total"
|
|
t-options='{"widget": "monetary", "display_currency": website_sale_order.currency_id}'/>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
<div name="cart_summary_info" t-if="not website_sale_order or not website_sale_order.website_order_line" class="alert alert-info">
|
|
Your cart is empty!
|
|
</div>
|
|
<div id="o_wsale_accordion_item"
|
|
class="accordion-collapse collapse mb-4 mb-lg-0"
|
|
data-bs-parent="#o_wsale_total_accordion">
|
|
<div t-att-class="len(website_sale_order.website_order_line) > 3 and 'o_wsale_scrollable_table mt-n4 me-n4 pt-4 pe-4'">
|
|
<table t-if="website_sale_order and website_sale_order.website_order_line"
|
|
class="table accordion-body mb-0"
|
|
id="cart_products">
|
|
<tbody>
|
|
<tr t-foreach="website_sale_order.website_order_line" t-as="line" t-att-class="line_last and 'border-transparent'">
|
|
<t t-set="o_cart_sum_padding_top"
|
|
t-value="'pt-3' if line_size > 1 and not line_first else 'pt-0'"/>
|
|
<td t-if="not line.product_id" colspan="2"/>
|
|
<t t-else="">
|
|
<td t-attf-class="td-img ps-0 #{o_cart_sum_padding_top}">
|
|
<span t-if="line._is_not_sellable_line() and line.product_id.image_128">
|
|
<img t-att-src="image_data_uri(line.product_id.image_128)" class="o_image_64_max 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': 'o_image_64_max rounded'}"
|
|
/>
|
|
</td>
|
|
<td t-attf-class="#{o_cart_sum_padding_top} td-product_name td-qty w-100"
|
|
name='website_sale_cart_summary_product_name'>
|
|
<h6>
|
|
<t t-out="int(line.product_uom_qty)" />
|
|
<t t-if="line._get_shop_warning(clear=False)">
|
|
<i class="fa fa-warning text-warning"
|
|
role="img"
|
|
t-att-title="line._get_shop_warning()"
|
|
aria-label="Warning"/>
|
|
</t>
|
|
x
|
|
<t t-out="line.name_short"/>
|
|
</h6>
|
|
</td>
|
|
</t>
|
|
<td t-attf-class="#{o_cart_sum_padding_top} td-price pe-0 text-end"
|
|
name="website_sale_cart_summary_line_price">
|
|
<span t-if="website.show_line_subtotals_tax_selection == 'tax_excluded'"
|
|
t-field="line.price_subtotal" style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
<span t-else=""
|
|
t-field="line.price_total" style="white-space: nowrap;"
|
|
t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
</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-esc='website_sale_order._get_shop_warning()'/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<t t-call="website_sale.total">
|
|
<t t-set="_cart_total_classes" t-valuef="border-top pt-3"/>
|
|
</t>
|
|
<div t-if="show_navigation_button" class="o_cta_navigation_container position-absolute position-lg-static start-0 bottom-0 col-12">
|
|
<t t-call="website_sale.navigation_buttons"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div t-attf-class="oe_cart col-12 col-lg-7">
|
|
<t t-out="0"/>
|
|
</div>
|
|
<!-- This div serves as an anchor for the navigation buttons on the mobile
|
|
view. -->
|
|
<div t-if="not show_shorter_cart_summary and show_navigation_button"
|
|
class="o_cta_navigation_placeholder d-block d-none d-lg-none order-lg-4"/>
|
|
</div>
|
|
</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>
|
|
|
|
<!-- /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_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']" class="d-flex justify-content-between align-items-center">
|
|
<h3>Thank you for your order.</h3>
|
|
<a role="button" class="d-none d-md-inline-block btn btn-primary ms-auto" href="/shop/print" target="_blank" aria-label="Print" title="Print"><i class="fa fa-print me-2"></i>Print</a>
|
|
</div>
|
|
<t t-if="tx_sudo.state == 'done'">
|
|
<div class="mb-4">
|
|
<h5>
|
|
<em>
|
|
<span>Order</span>
|
|
<span t-field="order.name" />
|
|
<t t-if="order.state == 'sale'">
|
|
<i class="fa fa-check-circle ms-1"/>
|
|
</t>
|
|
</em>
|
|
</h5>
|
|
</div>
|
|
</t>
|
|
<t t-if="tx_sudo.state != 'done'">
|
|
<div class="mb-4">
|
|
<h5>
|
|
<em>
|
|
<span>Order</span>
|
|
<span t-field="order.name" />
|
|
<t t-if="order.state == 'sale'">
|
|
<i class="fa fa-check-circle ms-1"/>
|
|
</t>
|
|
</em>
|
|
</h5>
|
|
</div>
|
|
</t>
|
|
<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).signup_url" class="btn btn-primary">Sign Up</a>
|
|
to follow your order.
|
|
</p>
|
|
</t>
|
|
<div class="oe_structure clearfix mt-3" id="oe_structure_website_sale_confirmation_1"/>
|
|
<h4 class="text-start mt-3">Payment Information</h4>
|
|
<table class="table">
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="2" class="ps-0">
|
|
<t t-esc="tx_sudo.provider_id.name" />
|
|
</td>
|
|
<td class="text-end pe-0" width="100">
|
|
<strong>Total:</strong>
|
|
</td>
|
|
<td class="text-end pe-0" width="100">
|
|
<strong t-field="tx_sudo.amount" t-options="{'widget': 'monetary', 'display_currency': order.currency_id}" />
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<t t-call="website_sale.payment_confirmation_status"/>
|
|
<div id="address_on_confirmation" class="mt-3">
|
|
<t t-call="website_sale.address_on_payment">
|
|
<t t-set="disable_edit" t-value="True"/>
|
|
</t>
|
|
</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)}"}' />
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Called in `website_sale.checkout_layout`. -->
|
|
<template id="total">
|
|
<div id="cart_total" t-if="website_sale_order and website_sale_order.website_order_line" t-att-class="_cart_total_classes">
|
|
<table class="table mb-0">
|
|
<tr id="order_total_untaxed">
|
|
<td id="cart_total_subtotal"
|
|
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 id="order_total_taxes">
|
|
<td colspan="2" class="text-muted border-0 ps-0 pt-0 pb-3">Taxes</td>
|
|
<td class="text-end border-0 pe-0 pt-0 pb-3">
|
|
<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 id="order_total" class="border-top">
|
|
<td colspan="2" class="border-0 ps-0 pt-3"><strong>Total</strong></td>
|
|
<td class="text-end border-0 px-0 pt-3">
|
|
<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[@id='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 class="oe_website_sale_tx_status 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-attf-class="card #{
|
|
(tx_sudo.state == 'pending' and 'bg-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 'bg-warning') or
|
|
(tx_sudo.state == 'authorized' and 'alert-success') or
|
|
'bg-danger'}">
|
|
<div class="card-header">
|
|
<a role="button" groups="base.group_system" class="btn btn-sm btn-link text-white float-end" target="_blank" aria-label="Edit" title="Edit"
|
|
t-attf-href="/web#model=payment.provider&id=#{tx_sudo.provider_id.id}&action=payment.action_payment_provider&view_type=form">
|
|
<i class="fa fa-pencil"></i>
|
|
</a>
|
|
<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-esc="tx_sudo.state_message"/>
|
|
</t>
|
|
</div>
|
|
<t t-if="tx_sudo.provider_code == 'custom'">
|
|
<div t-if="order.reference" class="card-body">
|
|
<b>Communication: </b><span t-esc='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="card-body" 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>
|
|
|
|
<template id="sale_order_portal_content_inherit_website_sale" name="Orders Followup Products Links" inherit_id="sale.sale_order_portal_content">
|
|
<xpath expr="//section[@id='details']//div[hasclass('table-responsive')]" position="attributes">
|
|
<attribute name="class" remove="table-responsive" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//section[@id='details']//td[@id='product_name']/*" position="replace">
|
|
<a t-if="line.product_id.website_published" t-att-href="line.product_id.website_url" class="d-block text-wrap" style="max-width: 35vw">
|
|
<span t-field="line.name" />
|
|
</a>
|
|
<t t-if="not line.product_id.website_published">
|
|
<span t-field="line.name" class="d-block text-wrap" style="max-width: 35vw"/>
|
|
</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_variant.sudo().ribbon_id or product.sudo().website_ribbon_id"/>
|
|
<t t-set="bg_color" t-value="ribbon['bg_color'] or ''"/>
|
|
<t t-set="text_color" t-value="ribbon['text_color']"/>
|
|
<t t-set="bg_class" t-value="ribbon['html_class']"/>
|
|
<t t-call="website_sale.shop_product_#{website.product_page_image_layout}"/>
|
|
</template>
|
|
|
|
<template id="website_sale.shop_product_image">
|
|
<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"
|
|
>
|
|
<span t-attf-class="o_ribbon #{ribbon['html_class']} z-index-1"
|
|
t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}"
|
|
t-out="ribbon['html'] or ''"
|
|
/>
|
|
<div t-field="product_image.image_1920"
|
|
class="d-flex align-items-start justify-content-center h-100 oe_unmovable"
|
|
t-options='{"widget": "image", "preview_image": "image_1024", "class": "oe_unmovable product_detail_img mh-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 + ' oe_unmovable'" t-options='{"widget": "image", "preview_image": "image_1024", "class": "oe_unmovable product_detail_img mh-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" class="carousel slide position-sticky mb-3 overflow-hidden" data-bs-ride="carousel" data-bs-interval="0" t-att-data-name="product_carousel_block_name">
|
|
<div class="o_carousel_product_outer carousel-outer position-relative flex-grow-1 overflow-hidden">
|
|
<span t-if="len(product_images) > 1"
|
|
t-attf-class="o_ribbon #{ribbon['html_class']} z-index-1"
|
|
t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}"
|
|
t-out="ribbon['html'] or ''"
|
|
/>
|
|
<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 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">
|
|
<span class="oi oi-chevron-left fa-2x oe_unmovable" role="img" aria-label="Previous" title="Previous"/>
|
|
</a>
|
|
<a class="carousel-control-next" href="#o-carousel-product" role="button" data-bs-slide="next">
|
|
<span class="oi oi-chevron-right fa-2x oe_unmovable" 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">
|
|
<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" active="False">
|
|
<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-2'"/>
|
|
</t>
|
|
</xpath>
|
|
<xpath expr="//div[@id='o-carousel-product']" position="attributes">
|
|
<attribute name="class" add="o_carousel_product_left_indicators d-flex" separator=" "/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="carousel_product_indicators" name="Carousel Product">
|
|
<div t-ignore="True" t-attf-class="o_carousel_product_indicators {{indicators_div_class}}">
|
|
<ol t-if="len(product_images) > 1" t-attf-class="carousel-indicators {{indicators_list_class}} position-static pt-2 pt-lg-0 mx-auto my-0 text-start">
|
|
<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": "o_image_64_cover", "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">
|
|
<div id="o-grid-product" class="o_wsale_product_page_grid mb-3" data-name="Product Grid" t-att-data-image_spacing="website.product_page_image_spacing" t-att-data-grid_columns="website.product_page_grid_columns">
|
|
<div class="container position-relative overflow-hidden">
|
|
<span t-attf-class="o_ribbon #{ribbon['html_class']}"
|
|
t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}"
|
|
t-out="ribbon['html'] or ''"
|
|
/>
|
|
<!-- One row for every two images -->
|
|
<t t-set="image_classes" t-value="'w-100'"/>
|
|
<t t-set="col_classes" t-value="website._get_product_page_grid_image_classes()"/>
|
|
<t t-foreach="range(ceil(len(product_images) / website.product_page_grid_columns))" t-as="row_idx">
|
|
<div class="row m-0">
|
|
<t t-foreach="range(row_idx * website.product_page_grid_columns, (row_idx + 1) * website.product_page_grid_columns)" t-as="image_idx">
|
|
<t t-set="product_image" t-value="image_idx < len(product_images) and product_images[image_idx] or False"/>
|
|
<div t-if="product_image" t-att-class="col_classes">
|
|
<t t-call="website_sale.shop_product_image"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</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[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[field.name]'>
|
|
<b><t t-esc='field.label'/>: </b>
|
|
<t t-if='field.field_id.ttype != "binary"'>
|
|
<span t-esc='product[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>
|
|
|
|
<template id="add_to_cart_redirect" inherit_id="website.layout" name="Cart Redirection" priority="1">
|
|
<xpath expr="//html" position="before">
|
|
<t t-set="html_data" t-value="dict(html_data, **{'data-add2cart-redirect': '1' if website.add_to_cart_action == 'stay' else '0' if website.add_to_cart_action == 'go_to_cart' else '2'})"/>
|
|
</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/category/#{slug(category)}';return false;"/>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="sale_order_re_order_btn" inherit_id="sale.sale_order_portal_template" name="Sale Order Order Again">
|
|
<xpath expr="//t[@t-set='entries']/div/div/div[hasclass('o_download_pdf')]" position="before">
|
|
<t t-if="request.website.enabled_portal_reorder_button and sale_order.with_user(request.env.user).sudo()._is_reorder_allowed()">
|
|
<button class="btn btn-primary o_wsale_reorder_button w-100" t-att-data-sale-order-id="sale_order.id">
|
|
<i class="fa fa-rotate-right me-1"/>
|
|
Order Again
|
|
</button>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
</odoo>
|