2497 lines
164 KiB
XML
2497 lines
164 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-_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 align-self-md-start #{not show_cart and 'd-none'} #{_item_class}">
|
|
<a href="/shop/cart" t-attf-class="#{_link_class}">
|
|
<i t-if="_icon" class="fa fa-shopping-cart"/>
|
|
<span t-if="_text">My Cart</span>
|
|
<sup class="my_cart_quantity badge text-bg-primary" t-esc="website_sale_cart_quantity" t-att-data-order-id="request.session.get('sale_order_id', '')"/>
|
|
</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_default" inherit_id="website.template_header_default">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item mx-lg-3'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_hamburger" inherit_id="website.template_header_hamburger">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_text" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_vertical" inherit_id="website.template_header_vertical">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item ms-lg-3'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_sidebar" inherit_id="website.template_header_sidebar">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<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="_item_class" t-value="'nav-item'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_slogan" inherit_id="website.template_header_slogan">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_contact" inherit_id="website.template_header_contact">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_text" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item ms-lg-3'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_boxed" inherit_id="website.template_header_boxed">
|
|
<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="'nav-item'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_centered_logo" inherit_id="website.template_header_centered_logo">
|
|
<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="_item_class" t-value="'nav-item'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_image" inherit_id="website.template_header_image">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item ms-lg-3'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_hamburger_full" inherit_id="website.template_header_hamburger_full">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_icon" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item ms-lg-3'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="template_header_magazine" inherit_id="website.template_header_magazine">
|
|
<xpath expr="//t[@t-foreach='website.menu_id.child_id']" position="after">
|
|
<t t-call="website_sale.header_cart_link">
|
|
<t t-set="_text" t-value="True"/>
|
|
<t t-set="_item_class" t-value="'nav-item ms-md-auto'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Search Bar input-group template -->
|
|
<template id="search" name="Search Box">
|
|
<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 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" 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_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 pe-lg-2 pb-lg-5 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 mb-3">
|
|
<t 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 ms-3"/>
|
|
</t>
|
|
|
|
<t t-if="is_view_active('website_sale.sort')" t-call="website_sale.sort">
|
|
<t t-set="_classes" t-valuef="d-none d-lg-inline-block ms-3"/>
|
|
</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 ms-3"/>
|
|
</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 ms-3 {{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" t-attf-class="position-absolute top-0 start-100 translate-middle badge 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-cache="pricelist,products" 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">
|
|
<xpath expr="//div[starts-with(@t-cache, 'pricelist,products')]" position="attributes">
|
|
<attribute name="t-cache" add="fiscal_position_id" separator=","/>
|
|
</xpath>
|
|
</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 ms-lg-3 {{_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">border-primary</t>
|
|
<div t-attf-class="o_wsale_apply_layout btn-group ms-3 {{_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"/>
|
|
|
|
<!-- OffCanvas Nav -->
|
|
<template id="website_sale.o_wsale_offcanvas" name="Offcanvas">
|
|
<aside id="o_wsale_offcanvas"
|
|
class="offcanvas offcanvas-end p-0">
|
|
|
|
<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 border-top 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 border-top 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 border-top 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-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 {{(_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 border-top 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 class="list-group list-group-flush"
|
|
t-if="a.display_type == 'radio' or a.display_type == 'pills' or a.display_type == 'select'">
|
|
<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 t-if="a.display_type == 'color'" class="pt-1 pb-3">
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<label t-attf-style="background-color:#{v.html_color or v.name}" 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>
|
|
</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 'disabled' }}"
|
|
t-att-aria-disabled="(not attrib_values and not isFilteringByPrice) 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 mb-2 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"
|
|
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" 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" t-att-href="keep('/shop'+ ('/category/'+slug(category)) if category else None, attrib=0)" t-attf-class="btn btn-{{navClass}} d-flex 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">
|
|
<div class="accordion-item nav-item mb-1" 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>
|
|
<t t-if="a.display_type == 'radio' or a.display_type == 'pills'">
|
|
<div 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>
|
|
</t>
|
|
<div t-if="a.display_type == 'color'" class="mb-3">
|
|
<t t-foreach="a.value_ids" t-as="v">
|
|
<label t-attf-style="background-color:#{v.html_color or v.name}" 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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</form>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="filter_products_price" name="Filter by Prices">
|
|
<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="pricelist.currency_id.symbol"
|
|
t-att-data-currency-position="pricelist.currency_id.position"
|
|
t-att-step="pricelist.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="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 Bas eunit 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">
|
|
<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 or 1, pricelist=pricelist)"/>
|
|
<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 t-attf-class="container py-4 oe_website_sale #{'discount' if combination_info['has_discounted_price'] else ''}" id="product_detail"
|
|
t-att-data-view-track="view_track and '1' or '0'"
|
|
t-att-data-product-tracking-info="json.dumps(request.env['product.template'].get_google_analytics_data(combination_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-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." />
|
|
<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="combination_info['base_unit_price']"
|
|
class="ms-1 text-muted o_base_unit_price_wrapper d-none" groups="website_sale.group_show_uom_price">
|
|
<t t-call='website_sale.base_unit_price'/>
|
|
</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]" />
|
|
<input t-if="product.product_tag_ids.ids" type="hidden" class="product_tag_id" name="product_tag_id" t-att-value="product.product_tag_ids.ids[0]" />
|
|
<t t-if="combination" t-call="sale.variants">
|
|
<t t-set="ul_class" t-valuef="flex-column" />
|
|
<t t-set="parent_combination" t-value="None" />
|
|
</t>
|
|
<t t-else="">
|
|
<ul class="d-none js_add_cart_variants mb-0" t-att-data-attribute_exclusions="{'exclusions: []'}"/>
|
|
</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"/>
|
|
<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 #{'btn-lg' if ctaSizeBig else ''}" href="#">
|
|
<i class="fa fa-shopping-cart me-2"/>
|
|
<span style="font-weight: bold">ADD TO CART</span>
|
|
</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>
|
|
</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>
|
|
<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="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 d-none"
|
|
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 #{'input-group-lg' if ctaSizeBig else ''}" contenteditable="false">
|
|
<a t-attf-href="#" class="btn btn-link js_add_cart_json" aria-label="Remove one" title="Remove one">
|
|
<i class="fa fa-minus"></i>
|
|
</a>
|
|
<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" t-attf-class="btn btn-outline-primary o_we_buy_now ms-1 #{'btn-lg' if ctaSizeBig else ''}" href="#">
|
|
<i class="fa fa-bolt me-2"/>
|
|
<span style="font-weight:bold">BUY NOW</span>
|
|
</a>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="website_sale.tax_indication" active="False">
|
|
<span groups="account.group_show_line_subtotals_tax_excluded" class="h6 text-muted">
|
|
VAT Excluded
|
|
</span>
|
|
<span groups="account.group_show_line_subtotals_tax_included" 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-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
<span itemprop="price" style="display:none;" t-esc="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": website.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, pricelist=pricelist)"/>
|
|
<t t-set="combination_info" t-value="variant_id._get_combination_info_variant(add_qty=add_qty, pricelist=pricelist)"/>
|
|
<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-esc="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-esc="diff_price > 0 and '+' or '-'"/>
|
|
<span t-esc="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-set="website_sale_order" t-value="website.sale_get_order()"/>
|
|
|
|
<div class="row">
|
|
<div class="col-xl">
|
|
<div class="wizard">
|
|
<div class="progress-wizard">
|
|
<a class="no-decoration" t-att-href="step>=10 and '/shop/cart' or '#'">
|
|
<div id="wizard-step10" t-att-class="'progress-wizard-step %s' % (step == 10 and 'active' or step>10 and 'complete' or 'disabled')">
|
|
<div class="progress-wizard-bar d-none d-md-block"/>
|
|
<span class="progress-wizard-dot d-none d-md-inline-block"></span>
|
|
<div class="text-center progress-wizard-steplabel">Review Order</div>
|
|
</div>
|
|
</a>
|
|
<a class="no-decoration" t-att-href="step>=20 and '/shop/checkout' or '#'">
|
|
<div id="wizard-step20" t-att-class="'progress-wizard-step %s' % (step == 20 and 'active' or step>20 and 'complete' or 'disabled')">
|
|
<div class="progress-wizard-bar d-none d-md-block"/>
|
|
<span class="progress-wizard-dot d-none d-md-inline-block"></span>
|
|
<div class="text-center progress-wizard-steplabel">Address</div>
|
|
</div>
|
|
</a>
|
|
<a class="no-decoration" t-att-href="step>=40 and '/shop/payment' or '#'">
|
|
<div id="wizard-step40" t-att-class="'progress-wizard-step %s' % (step == 40 and 'active' or step>40 and 'complete' or 'disabled')">
|
|
<div class="progress-wizard-bar d-none d-md-block"/>
|
|
<span class="progress-wizard-dot d-none d-md-inline-block"></span>
|
|
<div class="text-center progress-wizard-steplabel">Confirm Order</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="extra_info" name="Checkout Extra Info">
|
|
<t t-call="website.layout">
|
|
<t t-set="no_footer" t-value="1"/>
|
|
<div id="wrap">
|
|
<div class="container oe_website_sale py-2">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<t t-call="website_sale.wizard_checkout">
|
|
<t t-set="step" t-value="30"/>
|
|
</t>
|
|
</div>
|
|
<div class="col-12 col-xl-auto order-xl-2 d-none d-xl-block">
|
|
<t t-call="website_sale.cart_summary">
|
|
<t t-set="redirect" t-valuef="/shop/extra_info"/>
|
|
</t>
|
|
</div>
|
|
<div class="col-12 col-xl order-xl-1 oe_cart">
|
|
<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 row s_col_no_bgcolor">
|
|
<div class="mb-0 py-2 col-12 s_website_form_field" data-type="char" data-name="Field">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-auto s_website_form_label" 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="form-control s_website_form_input" name="client_order_ref"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-0 py-2 col-12 s_website_form_field s_website_form_custom" data-type="text" data-name="Field">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-auto s_website_form_label" 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="form-control s_website_form_input" name="Give us your feedback" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-0 py-2 col-12 s_website_form_field s_website_form_custom" data-type="binary" data-name="Field">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-auto s_website_form_label" 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="form-control s_website_form_input" name="a_document" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-0 py-2 col-12 s_website_form_submit s_website_form_no_submit_options">
|
|
<div style="width: 200px;" class="s_website_form_label"/>
|
|
<a role="button" href="/shop/checkout" class="btn btn-secondary float-start"><span class="fa fa-chevron-left"/> Previous</a>
|
|
<a role="button" class="btn btn-primary float-end s_website_form_send" href="/shop/confirm_order">Next <span class="fa fa-chevron-right" /></a>
|
|
<span id="s_website_form_result"></span>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure" id="oe_structure_website_sale_extra_info_1"/>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="extra_info_option" name="Extra Step Option" inherit_id="wizard_checkout" active="False">
|
|
<!-- Add a "flag element" to trigger style variation -->
|
|
<xpath expr="//div[hasclass('wizard')]/div" position="before">
|
|
<span class="o_wizard_has_extra_step d-none"/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='wizard-step20']" position="after">
|
|
<a class="no-decoration" t-att-href="step>=30 and '/shop/extra_info' or '#'">
|
|
<div id="wizard-step30" t-att-class="'progress-wizard-step %s' % (step == 30 and 'active' or step>30 and 'complete' or 'disabled')">
|
|
<div class="progress-wizard-bar d-none d-md-block"/>
|
|
<span class="progress-wizard-dot d-none d-md-inline-block"></span>
|
|
<div class="text-center progress-wizard-steplabel">Extra Info</div>
|
|
</div>
|
|
</a>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- We use this template where we want to give the user a link to the product of a sale order line. -->
|
|
<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. -->
|
|
<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-esc="name_line"/>
|
|
<br t-if="not name_line_last"/>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- This template is the one at the "Review order" step (the first one) on the checkout workflow. -->
|
|
<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" role="alert">
|
|
<strong>Warning!</strong> <t t-esc='website_sale_order._get_shop_warning()'/>
|
|
</div>
|
|
</t>
|
|
<table t-if="website_sale_order and website_sale_order.website_order_line" class="mb16 table table-striped table-sm js_cart_lines" id="cart_products">
|
|
<t t-set="show_qty" t-value="is_view_active('website_sale.product_quantity')"/>
|
|
<thead>
|
|
<tr>
|
|
<th class="td-img">Product</th>
|
|
<th></th>
|
|
<th class="text-center td-qty">
|
|
<t t-if="show_qty">
|
|
<span>Quantity</span>
|
|
</t>
|
|
</th>
|
|
<th class="text-center td-price">Price</th>
|
|
<th class="text-center td-action"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<t t-foreach="website_sale_order.website_order_line" t-as="line">
|
|
<tr t-att-class="'optional_product info' if line.linked_line_id else None">
|
|
<td t-if="not line.product_id" colspan="2" class='td-img'/>
|
|
<t t-else="">
|
|
<td align="center" class='td-img'>
|
|
<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="img o_image_64_max rounded"
|
|
t-att-alt="line.name_short"/>
|
|
</span>
|
|
<span t-else=""
|
|
t-field="line.product_id.image_128"
|
|
t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded o_image_64_max'}"
|
|
/>
|
|
</td>
|
|
<td class='td-product_name'>
|
|
<div>
|
|
<t t-call="website_sale.cart_line_product_link">
|
|
<strong t-field="line.name_short" />
|
|
</t>
|
|
</div>
|
|
<t t-call="website_sale.cart_line_description_following_lines">
|
|
<t t-set="div_class" t-value="'d-none d-md-block'"/>
|
|
</t>
|
|
</td>
|
|
</t>
|
|
<td class="text-center td-qty">
|
|
<div class="css_quantity input-group mx-auto justify-content-center">
|
|
<t t-if="not line._is_not_sellable_line()">
|
|
<t t-if="show_qty">
|
|
<a t-attf-href="#" class="btn btn-link js_add_cart_json d-none d-md-inline-block" aria-label="Remove one" title="Remove one">
|
|
<i class="fa fa-minus"></i>
|
|
</a>
|
|
<input type="text" 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="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" />
|
|
<t t-if="line._get_shop_warning(clear=False)">
|
|
<a t-attf-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='' t-attf-href="#" class="btn btn-link float_left js_add_cart_json d-none d-md-inline-block" aria-label="Add one" title="Add one">
|
|
<i class="fa fa-plus"></i>
|
|
</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="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" />
|
|
</t>
|
|
</t>
|
|
<t t-else="">
|
|
<span class="text-muted w-100" t-esc="int(line.product_uom_qty)"/>
|
|
<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.product_uom_qty" />
|
|
</t>
|
|
</div>
|
|
</td>
|
|
<td class="text-center td-price" name="price">
|
|
<t t-set="combination" t-value="line.product_id.product_template_attribute_value_ids + line.product_no_variant_attribute_value_ids"/>
|
|
<t t-set="combination_info" t-value="line.product_id.product_tmpl_id._get_combination_info(combination, pricelist=website_sale_order.pricelist_id, add_qty=line.product_uom_qty)"/>
|
|
|
|
<t groups="account.group_show_line_subtotals_tax_excluded">
|
|
<span t-field="line.price_reduce_taxexcl" style="white-space: nowrap;" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
</t>
|
|
<t groups="account.group_show_line_subtotals_tax_included">
|
|
<span t-field="line.price_reduce_taxinc" style="white-space: nowrap;" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}"/>
|
|
</t>
|
|
<t t-if="line.discount">
|
|
<del t-attf-class="#{'text-danger mr8'}" style="white-space: nowrap;" t-esc="combination_info['list_price']" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" />
|
|
</t>
|
|
<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>
|
|
</small>
|
|
</td>
|
|
<td class="td-action">
|
|
<a href='#' aria-label="Remove from cart" title="Remove from cart" class='js_delete_product no-decoration'> <small><i class='fa fa-trash-o'></i></small></a>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</tbody>
|
|
</table>
|
|
</template>
|
|
|
|
<template id="cart" name="Shopping Cart">
|
|
<t t-call="website.layout">
|
|
<div id="wrap">
|
|
<div class="container oe_website_sale py-2">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<t t-call="website_sale.wizard_checkout">
|
|
<t t-set="step" t-value="10" />
|
|
</t>
|
|
</div>
|
|
<div class="col-12 col-xl-8 oe_cart">
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<div t-if="abandoned_proceed or access_token" class="mt8 mb8 alert alert-info" 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="d-xl-none mb-2" id="o_cart_summary">
|
|
<t t-call="website_sale.short_cart_summary"/>
|
|
</div>
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<a role="button" href="/shop" class="btn btn-secondary mb32">
|
|
<span class="fa fa-chevron-left" />
|
|
<span>Continue<span class="d-none d-md-inline"> Shopping</span></span>
|
|
</a>
|
|
</div>
|
|
<t t-set="redirect_to_sign_in" t-value="website.account_on_checkout == 'mandatory' and website.is_public_user()"/>
|
|
<t t-set="redirect_url" t-value="'/web/login?redirect=/shop/checkout' if redirect_to_sign_in else '/shop/checkout?express=1'"/>
|
|
<div class="d-flex flex-column"
|
|
t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<a class="btn btn-primary float-end" role="button" t-attf-href="{{redirect_url}}">
|
|
<t t-if="redirect_to_sign_in">
|
|
<span>Sign In</span>
|
|
<span class="fa fa-sign-in"/>
|
|
</t>
|
|
<t t-else="">
|
|
<span>Process Checkout</span>
|
|
<span class="fa fa-chevron-right"/>
|
|
</t>
|
|
</a>
|
|
<t t-call="payment.express_checkout"/>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure" id="oe_structure_website_sale_cart_1"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-none d-xl-block col-xl-4" id="o_cart_summary">
|
|
<t t-call="website_sale.short_cart_summary"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure" id="oe_structure_website_sale_cart_2"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- this template is the one when we mouse over "My Cart" on the top right -->
|
|
<template id="cart_popover" name="Cart Popover">
|
|
<div t-if="not website_sale_order or not website_sale_order.website_order_line" class="alert alert-info">
|
|
Your cart is empty!
|
|
</div>
|
|
<t t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<t t-foreach="website_sale_order.website_order_line" t-as="line">
|
|
<div class="row mb8 cart_line">
|
|
<div class="col-3 text-center">
|
|
<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="img o_image_64_max rounded mb-2 img-fluid" t-att-alt="line.name_short"/>
|
|
</span>
|
|
<span t-else=""
|
|
t-field="line.product_id.image_128"
|
|
t-options="{'widget': 'image', 'qweb_img_responsive': True, 'class': 'rounded o_image_64_max mb-2'}"
|
|
/>
|
|
</div>
|
|
<div class="col-9">
|
|
<div>
|
|
<t t-call="website_sale.cart_line_product_link">
|
|
<span class="h6" t-esc="line.name_short" />
|
|
</t>
|
|
</div>
|
|
Qty: <t t-esc="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" />
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<div class="text-center">
|
|
<span class="h6">
|
|
<t t-call="website_sale.total">
|
|
<t t-set="hide_promotions" t-value="True"/>
|
|
</t>
|
|
</span>
|
|
<a role="button" class="btn btn-primary" href="/shop/cart">
|
|
View Cart (<span class="o_wsale_cart_quantity" t-esc="website_sale_order.cart_quantity" /> item(s))
|
|
</a>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="suggested_products_list" inherit_id="website_sale.cart_lines" name="Accessory Products in my cart">
|
|
<xpath expr="//table[@id='cart_products']" position="after">
|
|
<h5 class='text-muted js_cart_lines' t-if="suggested_products">Suggested Accessories:</h5>
|
|
<table t-if="suggested_products" id="suggested_products" class="js_cart_lines table table-striped table-sm">
|
|
<tbody>
|
|
<tr t-foreach="suggested_products" t-as="product" t-att-data-publish="product.website_published and 'on' or 'off'">
|
|
<t t-set="combination_info" t-value="product._get_combination_info_variant(pricelist=website_sale_order.pricelist_id)"/>
|
|
<td class='td-img text-center'>
|
|
<a t-att-href="product.website_url">
|
|
<span t-field="product.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded o_image_64_max'}" />
|
|
</a>
|
|
</td>
|
|
<td class='td-product_name'>
|
|
<div>
|
|
<a t-att-href="product.website_url">
|
|
<strong t-esc="combination_info['display_name']" />
|
|
</a>
|
|
</div>
|
|
<div class="text-muted d-none d-md-block" t-field="product.description_sale" />
|
|
</td>
|
|
<td class='td-price'>
|
|
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}" style="white-space: nowrap;" t-esc="combination_info['list_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
|
<span t-esc="combination_info['price']" style="white-space: nowrap;" t-options="{'widget': 'monetary','display_currency': website.currency_id}"/>
|
|
</td>
|
|
<td class="w-25 text-center">
|
|
<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="btn btn-link js_add_suggested_products">
|
|
<strong>Add to Cart</strong>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id='coupon_form' name='Coupon form'>
|
|
<form t-att-action="'/shop/pricelist%s' % (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">
|
|
<input name="promo" class="form-control" type="text" placeholder="code..." t-att-value="website_sale_order.pricelist_id.code or None"/>
|
|
<a href="#" role="button" class="btn btn-secondary a-submit">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>
|
|
|
|
<template id="checkout">
|
|
<t t-call="website.layout">
|
|
<t t-set="additional_title">Shop - Checkout</t>
|
|
<t t-set="no_footer" t-value="1"/>
|
|
<div id="wrap">
|
|
<div class="container oe_website_sale py-2">
|
|
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_id or only_services)" />
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<t t-call="website_sale.wizard_checkout">
|
|
<t t-set="step" t-value="20" />
|
|
</t>
|
|
</div>
|
|
<div class="col-12 col-xl-auto order-xl-2 d-none d-xl-block">
|
|
<t t-call="website_sale.cart_summary">
|
|
<t t-set="redirect" t-valuef="/shop/checkout"/>
|
|
</t>
|
|
</div>
|
|
<div class="col-12 col-xl order-xl-1 oe_cart">
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<h3 class="o_page_header mt8">Billing Address</h3>
|
|
</div>
|
|
<div class="col-lg-6 one_kanban">
|
|
<t t-call="website_sale.address_kanban">
|
|
<t t-set='contact' t-value="order.partner_id"/>
|
|
<t t-set='selected' t-value="1"/>
|
|
<t t-set='readonly' t-value="1"/>
|
|
<t t-set='allow_edit' t-value="1"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
<t t-if="not only_services" groups="account.group_delivery_invoice_address">
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<h3 class="o_page_header mt16 mb4">Shipping Address</h3>
|
|
</div>
|
|
</div>
|
|
<div class="row all_shipping">
|
|
<div class="col-lg-12">
|
|
<div class="row mt8">
|
|
<div class="col-md-12 col-lg-12 one_kanban">
|
|
<form action="/shop/address" method="post" class=''>
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" t-nocache="The csrf token must always be up to date."/>
|
|
<a role="button" href="#" class='a-submit btn btn-secondary mb-2 btn-block'>
|
|
<i class="fa fa-plus-square"/>
|
|
<span>Add an address</span>
|
|
</a>
|
|
</form>
|
|
</div>
|
|
<t t-foreach="shippings" t-as="ship">
|
|
<div class="col-md-12 col-lg-6 one_kanban mb-2">
|
|
<t t-call="website_sale.address_kanban">
|
|
<t t-set='contact' t-value="ship"/>
|
|
<t t-set='selected' t-value="order.partner_shipping_id==ship"/>
|
|
<t t-set='readonly' t-value="bool(len(shippings)==1)"/>
|
|
<t t-set='edit_billing' t-value="bool(ship==order.partner_id)"/>
|
|
<t t-set="allow_edit" t-value="not order.partner_id or (ship.id in order.partner_id.child_ids.ids)" />
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<div class="d-flex justify-content-between mt-3">
|
|
<a role="button" href="/shop/cart" class="btn btn-secondary mb32">
|
|
<i class="fa fa-chevron-left"/>
|
|
<span>Return to Cart</span>
|
|
</a>
|
|
<a role="button" href="/shop/confirm_order" class="btn btn-primary mb32">
|
|
<span>Confirm</span>
|
|
<i class="fa fa-chevron-right"/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="address_kanban" name="Kanban address">
|
|
<form action="/shop/checkout" 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" />
|
|
<t t-if='edit_billing'>
|
|
<input type="hidden" name="callback" value="/shop/checkout?use_billing" />
|
|
</t>
|
|
<input type='submit'/>
|
|
</form>
|
|
<div t-attf-class="card #{selected and 'border border-primary' or 'js_change_shipping'}">
|
|
<div class='card-body' style='min-height: 130px;'>
|
|
<a t-if="allow_edit" href="#" class="btn btn-link float-end p-0 js_edit_address no-decoration" role="button" title="Edit this address" aria-label="Edit this address"><i class='fa fa-edit'/></a>
|
|
<t t-esc="contact" t-options="dict(widget='contact', fields=['name', 'address'], no_marker=True)"/>
|
|
</div>
|
|
<div class='card-footer' t-if='not readonly'>
|
|
<span class='btn-ship' t-att-style="'' if selected else 'display:none;'">
|
|
<a role="button" href='#' class="btn btn-block btn-primary">
|
|
<i class='fa fa-check'></i> Ship to this address
|
|
</a>
|
|
</span>
|
|
<span class='btn-ship' t-att-style="'' if not selected else 'display:none;'">
|
|
<a role="button" href='#' class="btn btn-block btn-secondary">
|
|
Select this address
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="address" name="Address Management">
|
|
<t t-set="no_footer" t-value="1"/>
|
|
<t t-call="website.layout">
|
|
<div id="wrap">
|
|
<div class="container oe_website_sale py-2">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<t t-call="website_sale.wizard_checkout">
|
|
<t t-set="step" t-value="20" />
|
|
</t>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-12 col-xl-auto order-xl-2 d-none d-xl-block">
|
|
<t t-call="website_sale.cart_summary">
|
|
<t t-set="hide_promotions">True</t>
|
|
<t t-set="redirect" t-valuef="/shop/address"/>
|
|
</t>
|
|
</div>
|
|
<div class="col-12 col-xl order-xl-1 oe_cart">
|
|
<div>
|
|
<t t-if="mode == ('new', 'billing')">
|
|
<h2 class="o_page_header mt8">
|
|
<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>
|
|
</h2>
|
|
</t>
|
|
<t t-if="mode == ('edit', 'billing')">
|
|
<h2 class="o_page_header mt8">Your Address</h2>
|
|
</t>
|
|
<t t-if="mode[1] == 'shipping'">
|
|
<h2 class="o_page_header mt8">Shipping Address </h2>
|
|
</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="mb-3 #{error.get('name') and 'o_has_error' or ''} col-lg-12 div_name">
|
|
<label class="col-form-label" for="name">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="mb-3 #{error.get('email') and 'o_has_error' or ''} col-lg-6" 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="mb-3 #{error.get('phone') and 'o_has_error' or ''} col-lg-6" 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') or (mode == ('edit', 'billing') and (can_edit_vat or 'vat' in checkout and checkout['vat']))">
|
|
<div t-attf-class="mb-3 #{error.get('company_name') and 'o_has_error' or ''} col-lg-6 mb-0">
|
|
<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="mb-3 #{error.get('vat') and 'o_has_error' or ''} col-lg-6 div_vat mb-0">
|
|
<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 class="w-100"/>
|
|
<div t-attf-class="mb-3 #{error.get('street') and 'o_has_error' or ''} col-lg-12 div_street">
|
|
<label class="col-form-label" for="street">Street <span class="d-none d-md-inline"> and Number</span></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-3 #{error.get('street2') and 'o_has_error' or ''} col-lg-12 div_street2">
|
|
<label class="col-form-label label-optional" for="street2">Street 2</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="mb-3 #{error.get('zip') and 'o_has_error' or ''} col-md-4 div_zip">
|
|
<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="mb-3 #{error.get('city') and 'o_has_error' or ''} col-md-8 div_city">
|
|
<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="mb-3 #{error.get('zip') and 'o_has_error' or ''} col-md-4 div_zip">
|
|
<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="mb-3 #{error.get('country_id') and 'o_has_error' or ''} col-lg-6 div_country">
|
|
<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="mb-3 #{error.get('state_id') and 'o_has_error' or ''} col-lg-6 div_state" 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="checkbox">
|
|
<label>
|
|
<input type="checkbox" id='shipping_use_same' class="mr8" name='use_same' value="1" checked='checked'/>Ship to the same address
|
|
<span class='ship_to_other text-muted' style="display: none">&nbsp;(<i>Your shipping address will be requested later) </i></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</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="callback" t-att-value="callback" />
|
|
<!-- Example -->
|
|
<input type="hidden" name="field_required" t-att-value="'phone,name'" />
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<a role="button" t-att-href="mode == ('new', 'billing') and '/shop/cart' or '/shop/checkout'" class="btn btn-secondary mb32">
|
|
<i class="fa fa-chevron-left"/>
|
|
<span>Back</span>
|
|
</a>
|
|
<a role="button" href="#" class="btn btn-primary mb32 a-submit a-submit-disable a-submit-loading">
|
|
<span>Next</span>
|
|
<i class="fa fa-chevron-right"/>
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="address_b2b" inherit_id="address" name="Show b2b fields" />
|
|
|
|
<template id="address_on_payment" name="Address on payment">
|
|
<div class="card">
|
|
<div class="card-body" id="shipping_and_billing">
|
|
<a class="float-end no-decoration" href="/shop/checkout"><i class="fa fa-edit"/> Edit</a>
|
|
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_id or only_services)" />
|
|
<div>
|
|
<b>Billing<t t-if="same_shipping and not only_services"> & Shipping</t>: </b>
|
|
<span t-esc="order.partner_id" t-options="dict(widget='contact', fields=['address'], no_marker=True, separator=', ')" class="address-inline"/>
|
|
</div>
|
|
<div t-if="not same_shipping and not only_services" groups="account.group_delivery_invoice_address">
|
|
<b>Shipping: </b>
|
|
<span t-esc="order.partner_shipping_id" t-options="dict(widget='contact', fields=['address'], no_marker=True, separator=', ')" class="address-inline"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="payment" name="Payment">
|
|
<t t-call="website.layout">
|
|
<t t-set="additional_title">Shop - Select Payment Provider</t>
|
|
<t t-set="no_footer" t-value="1"/>
|
|
|
|
<div id="wrap">
|
|
<div class="container oe_website_sale py-2">
|
|
<div class="row">
|
|
<div class='col-12'>
|
|
<t t-call="website_sale.wizard_checkout">
|
|
<t t-set="step" t-value="40" />
|
|
</t>
|
|
</div>
|
|
<div class="col-12" t-if="errors">
|
|
<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>
|
|
<div class="col-12 col-xl-auto order-xl-2">
|
|
<t t-call="website_sale.cart_summary"/>
|
|
</div>
|
|
<div class="col-12 col-xl order-xl-1 oe_cart">
|
|
<div id='address_on_payment'>
|
|
<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="providers or tokens" id="payment_method" class="mt-3">
|
|
<h3 class="mb24">Pay with </h3>
|
|
<div class="o_not_editable">
|
|
<t t-call="payment.checkout">
|
|
<t t-set="footer_template_id" t-value="'website_sale.payment_footer'"/>
|
|
<t t-set="submit_button_label">Pay Now</t>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
<div t-else="" class="alert alert-warning">
|
|
<strong>No suitable payment option could be found.</strong><br/>
|
|
If you believe that it is an error, please contact the website administrator.
|
|
</div>
|
|
</t>
|
|
|
|
<div t-if="not providers" class="mt-2">
|
|
<a role="button" class="btn-link"
|
|
groups="base.group_system"
|
|
t-attf-href="/web#action=#{payment_action_id}">
|
|
<i class="fa fa-arrow-right"></i> Add payment providers
|
|
</a>
|
|
</div>
|
|
<div class="js_payment mt-3" t-if="not errors and not website_sale_order.amount_total" id="payment_method" name="o_website_sale_free_cart">
|
|
<form name="o_wsale_confirm_order" 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-call="website_sale.payment_footer">
|
|
<t t-set="submit_button_label">Confirm Order</t>
|
|
</t>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure" id="oe_structure_website_sale_payment_2"/>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="payment_footer" name="Payment">
|
|
<div name="o_checkbox_container"
|
|
class="form-check mt-2 o_accept_tc_button"/>
|
|
<div class="float-start mt-2">
|
|
<a role="button" href="/shop/cart" class="btn btn-secondary">
|
|
<i class="fa fa-chevron-left"/> Return to Cart
|
|
</a>
|
|
</div>
|
|
<div class="float-end mt-2">
|
|
<button name="o_payment_submit_button"
|
|
type="submit"
|
|
class="btn btn-primary"
|
|
disabled="true"
|
|
data-icon-class="fa-chevron-right">
|
|
<t t-esc="submit_button_label"/> <i class="fa fa-chevron-right"/>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="payment_sale_note" inherit_id="payment_footer" name="Accept Terms & Conditions" active="False">
|
|
<xpath expr="//div[@name='o_checkbox_container']" position="inside">
|
|
<input type="checkbox" id="checkbox_tc" class="form-check-input"/>
|
|
<label for="checkbox_tc" class="form-check-label">
|
|
I agree to the <a target="_BLANK" href="/terms">terms & conditions</a>
|
|
</label>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="short_cart_summary" name="Short Cart right column">
|
|
<div class="card js_cart_summary" t-if="website_sale_order and website_sale_order.website_order_line" >
|
|
<div class="card-body">
|
|
<h4 class="d-none d-xl-block">Order Total</h4>
|
|
<hr class="d-none d-xl-block"/>
|
|
<div>
|
|
<t t-call="website_sale.total">
|
|
<t t-set="no_rowspan" t-value="1"/>
|
|
</t>
|
|
<t t-set="redirect_to_sign_in" t-value="website.account_on_checkout == 'mandatory' and website.is_public_user()"/>
|
|
<t t-set="redirect_url" t-value="'/web/login?redirect=/shop/checkout' if redirect_to_sign_in else '/shop/checkout?express=1'"/>
|
|
<a role="button" t-if="website_sale_order and website_sale_order.website_order_line"
|
|
class="btn btn-secondary float-end d-none d-xl-inline-block"
|
|
t-attf-href="{{redirect_url}}">
|
|
<t t-if="redirect_to_sign_in">
|
|
<span>Sign In</span>
|
|
<span class="fa fa-sign-in"/>
|
|
</t>
|
|
<t t-else="">
|
|
<span>Proceed to Checkout</span>
|
|
<span class="fa fa-chevron-right"/>
|
|
</t>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- This template is the one present on the right during the payment process.
|
|
Here it is important to not show too much information to the user, because we want him to pay!
|
|
We shouldn't display link to products or long descriptions.
|
|
-->
|
|
<template id="cart_summary" name="Cart right column">
|
|
<div class="card">
|
|
<div class="card-body p-xl-0">
|
|
<div class="toggle_summary d-xl-none">
|
|
<b>Your order: </b> <span id="amount_total_summary" class="monetary_field" t-field="website_sale_order.amount_total" t-options='{"widget": "monetary", "display_currency": website_sale_order.pricelist_id.currency_id}'/>
|
|
<span class='fa fa-chevron-down fa-border float-end' role="img" aria-label="Details" title="Details"></span>
|
|
</div>
|
|
<div t-if="not website_sale_order or not website_sale_order.website_order_line" class="alert alert-info">
|
|
Your cart is empty!
|
|
</div>
|
|
<div class="toggle_summary_div d-none d-xl-block">
|
|
<table class="table table-striped table-sm" id="cart_products" t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<thead>
|
|
<tr>
|
|
<th class="border-top-0 td-img">Product</th>
|
|
<th class="border-top-0"></th>
|
|
<th class="border-top-0 td-qty">Quantity</th>
|
|
<th class="border-top-0 text-center td-price">Price</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr t-foreach="website_sale_order.website_order_line" t-as="line">
|
|
<td colspan="2" t-if="not line.product_id.product_tmpl_id"/>
|
|
<t t-else="">
|
|
<td class='td-img text-center'>
|
|
<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="img o_image_64_max rounded" t-att-alt="line.name_short"/>
|
|
</span>
|
|
<span t-else=""
|
|
t-field="line.product_id.image_128"
|
|
t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded o_image_64_max'}"
|
|
/>
|
|
</td>
|
|
<td class='td-product_name'>
|
|
<div>
|
|
<strong t-field="line.name_short" />
|
|
</div>
|
|
</td>
|
|
</t>
|
|
<td class='td-qty'>
|
|
<div t-esc="line.product_uom_qty" />
|
|
<t t-if='line._get_shop_warning(clear=False)'>
|
|
<i class='fa fa-warning text-warning' t-att-title="line._get_shop_warning()" role="img" aria-label="Warning"/>
|
|
</t>
|
|
</td>
|
|
<td class="text-center td-price">
|
|
<span t-field="line.price_reduce_taxexcl" style="white-space: nowrap;" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" groups="account.group_show_line_subtotals_tax_excluded" />
|
|
<span t-field="line.price_reduce_taxinc" style="white-space: nowrap;" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" groups="account.group_show_line_subtotals_tax_included" />
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<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>
|
|
<t t-call="website_sale.total">
|
|
<t t-set='redirect' t-value="redirect or '/shop/payment'"></t>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="confirmation">
|
|
<t t-call="website.layout">
|
|
<t t-set="additional_title">Shop - Confirmed</t>
|
|
<div id="wrap">
|
|
<div class="container oe_website_sale py-2">
|
|
<h1><span>Order</span> <em t-field="order.name" /> <t t-if="order.state == 'sale'"><span>Confirmed</span></t></h1>
|
|
|
|
<div class="row">
|
|
<div class="col-12 col-xl">
|
|
<div class="oe_cart">
|
|
<t t-set="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
|
|
<t t-if="payment_tx_id.state == 'done'">
|
|
<div class="thanks_msg">
|
|
<h2>Thank you for your order.
|
|
<a role="button" class="btn btn-primary d-none d-md-inline-block" href="/shop/print" target="_blank" aria-label="Print" title="Print"><i class="fa fa-print"></i> Print</a>
|
|
</h2>
|
|
</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"/>
|
|
<h3 class="text-start mt-3">
|
|
<strong>Payment Information:</strong>
|
|
</h3>
|
|
<t t-set="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
|
|
<table class="table">
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="2">
|
|
<t t-esc="payment_tx_id.provider_id.display_as or payment_tx_id.provider_id.name" />
|
|
</td>
|
|
<td class="text-end" width="100">
|
|
<strong>Total:</strong>
|
|
</td>
|
|
<td class="text-end" width="100">
|
|
<strong t-field="payment_tx_id.amount" t-options="{'widget': 'monetary', 'display_currency': order.pricelist_id.currency_id}" />
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<t t-call="website_sale.payment_confirmation_status"/>
|
|
<div class="card mt-3">
|
|
<div class="card-body">
|
|
<t t-set="same_shipping" t-value="bool(order.partner_shipping_id==order.partner_invoice_id or only_services)" />
|
|
<div><b>Billing <t t-if="same_shipping and not only_services"> & Shipping</t>: </b><span t-esc='order.partner_invoice_id' t-options="dict(widget='contact', fields=['address'], no_marker=True, separator=', ')" class="address-inline"/></div>
|
|
<div t-if="not same_shipping and not only_services" groups="account.group_delivery_invoice_address"><b>Shipping: </b><span t-esc='order.partner_shipping_id' t-options="dict(widget='contact', fields=['address'], no_marker=True, separator=', ')" class="address-inline"/></div>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure mt-3" id="oe_structure_website_sale_confirmation_2"/>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-xl-auto">
|
|
<t t-set="website_sale_order" t-value="order"/>
|
|
<t t-call="website_sale.cart_summary">
|
|
<t t-set="hide_promotions" t-value="1"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure" id="oe_structure_website_sale_confirmation_3"/>
|
|
</div>
|
|
<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(order.amount_total/100)*100 - 50), round(order.amount_total/100)*100 + 50)}"}' />
|
|
</t>
|
|
</template>
|
|
|
|
<template id="total">
|
|
<div id="cart_total" t-att-class="extra_class or ''" t-if="website_sale_order and website_sale_order.website_order_line">
|
|
<table class="table mb-0">
|
|
<tr id="empty">
|
|
<t t-if='not no_rowspan'><td rowspan="10" class="border-0"/></t>
|
|
<td class="col-md-2 col-3 border-0"></td>
|
|
<td class="col-md-2 col-3 border-0" ></td>
|
|
</tr>
|
|
<tr id="order_total_untaxed">
|
|
<td class="text-end border-0">Subtotal:</td>
|
|
<td class="text-xl-end border-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 class="text-end border-0">Taxes:</td>
|
|
<td class="text-xl-end border-0">
|
|
<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">
|
|
<td class="text-end border-top border-bottom-0"><strong>Total:</strong></td>
|
|
<td class="text-xl-end border-top border-bottom-0">
|
|
<strong t-field="website_sale_order.amount_total" class="monetary_field"
|
|
t-options='{"widget": "monetary", "display_currency": website_sale_order.pricelist_id.currency_id}'/>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="reduction_code" inherit_id="website_sale.total" name="Promo Code">
|
|
<xpath expr="//div[@id='cart_total']//table/tr[last()]" position="after">
|
|
<tr t-if="not hide_promotions">
|
|
<td colspan="3" class="text-center text-xl-end border-0">
|
|
<span class=''>
|
|
<t t-set='force_coupon' t-value="website_sale_order.pricelist_id.code or request.params.get('code_not_available')"/>
|
|
<t t-if="not force_coupon">
|
|
<a href="#" class="show_coupon">I have a promo code</a>
|
|
</t>
|
|
<div t-attf-class="coupon_form #{not force_coupon and 'd-none'}">
|
|
<t t-call="website_sale.coupon_form"/>
|
|
</div>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</xpath>
|
|
</template>
|
|
|
|
<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="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
|
|
<div t-attf-class="card #{
|
|
(payment_tx_id.state == 'pending' and 'bg-info') or
|
|
(payment_tx_id.state == 'done' and order.amount_total == payment_tx_id.amount and 'alert-success') or
|
|
(payment_tx_id.state == 'done' and order.amount_total != payment_tx_id.amount and 'bg-warning') or
|
|
(payment_tx_id.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-att-href="'/web#model=%s&id=%s&action=%s&view_type=form' % ('payment.provider', payment_tx_id.provider_id.id, 'payment.action_payment_provider')">
|
|
<i class="fa fa-pencil"></i>
|
|
</a>
|
|
<t t-if="payment_tx_id.state == 'pending'">
|
|
<t t-out="payment_tx_id.provider_id.sudo().pending_msg"/>
|
|
</t>
|
|
<t t-if="payment_tx_id.state == 'done'">
|
|
<span t-if='payment_tx_id.provider_id.sudo().done_msg' t-out="payment_tx_id.provider_id.sudo().done_msg"/>
|
|
</t>
|
|
<t t-if="payment_tx_id.state == 'done' and order.amount_total != payment_tx_id.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="payment_tx_id.state == 'cancel'">
|
|
<t t-out="payment_tx_id.provider_id.sudo().cancel_msg"/>
|
|
</t>
|
|
<t t-if="payment_tx_id.state == 'authorized'">
|
|
<t t-if="payment_tx_id.provider_id.sudo().auth_msg" t-out="payment_tx_id.provider_id.sudo().auth_msg"/>
|
|
<span t-else="">Your payment has been authorized.</span>
|
|
</t>
|
|
<t t-if="payment_tx_id.state == 'error'">
|
|
<span t-esc="payment_tx_id.state_message"/>
|
|
</t>
|
|
</div>
|
|
<t t-if="payment_tx_id.provider_code == 'custom'">
|
|
<div t-if="order.reference" class="card-body">
|
|
<b>Communication: </b><span t-esc='order.reference'/>
|
|
</div>
|
|
<div t-if="payment_tx_id.provider_id.sudo().qr_code">
|
|
<t t-set="qr_code" t-value="payment_tx_id.company_id.partner_id.bank_ids[:1].build_qr_code_base64(order.amount_total,payment_tx_id.reference, None, payment_tx_id.currency_id, payment_tx_id.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-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-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">
|
|
<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#{' 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="fa fa-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="fa fa-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">
|
|
<!-- 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" 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']/ul/li" position="after">
|
|
<li t-if="request.website.enabled_portal_reorder_button and sale_order.with_user(request.env.user).sudo()._is_reorder_allowed()" class="list-group-item flex-grow-1">
|
|
<button class="btn btn-primary w-100 o_wsale_reorder_button" t-att-data-sale-order-id="sale_order.id">
|
|
<i class="fa fa-rotate-right me-1"/>
|
|
Order Again
|
|
</button>
|
|
</li>
|
|
</xpath>
|
|
</template>
|
|
</odoo>
|