summaryrefslogtreecommitdiff
path: root/addons/website_sale/views
diff options
context:
space:
mode:
Diffstat (limited to 'addons/website_sale/views')
-rw-r--r--addons/website_sale/views/account_views.xml33
-rw-r--r--addons/website_sale/views/crm_team_views.xml29
-rw-r--r--addons/website_sale/views/digest_views.xml17
-rw-r--r--addons/website_sale/views/onboarding_views.xml21
-rw-r--r--addons/website_sale/views/product_views.xml280
-rw-r--r--addons/website_sale/views/res_config_settings_views.xml354
-rw-r--r--addons/website_sale/views/sale_order_views.xml298
-rw-r--r--addons/website_sale/views/sale_report_views.xml111
-rw-r--r--addons/website_sale/views/snippets/s_dynamic_snippet_products.xml24
-rw-r--r--addons/website_sale/views/snippets/s_products_searchbar.xml54
-rw-r--r--addons/website_sale/views/snippets/snippets.xml112
-rw-r--r--addons/website_sale/views/templates.xml1942
-rw-r--r--addons/website_sale/views/website_sale_visitor_views.xml125
13 files changed, 3400 insertions, 0 deletions
diff --git a/addons/website_sale/views/account_views.xml b/addons/website_sale/views/account_views.xml
new file mode 100644
index 00000000..1b03a400
--- /dev/null
+++ b/addons/website_sale/views/account_views.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="action_invoices_ecommerce" model="ir.actions.act_window">
+ <field name="name">Invoices</field>
+ <field name="res_model">account.move</field>
+ <field name="view_mode">tree,form</field>
+ <field name="domain">[('team_id.website_ids', '!=', False)]</field>
+ <field name="view_id" ref="account.view_invoice_tree"/>
+ <field name="context">{'move_type':'out_invoice'}</field>
+ <field name="search_view_id" ref="account.view_account_invoice_filter"/>
+ </record>
+
+ <record id="website_product_pricelist3" model="ir.actions.act_window">
+ <field name="name">Pricelists</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">product.pricelist</field>
+ <field name="view_mode">tree,form</field>
+ <field name="domain">[('website_id', '!=', False)]</field>
+ <field name="search_view_id" ref="product.product_pricelist_view_search" />
+ <field name="context">{"default_base":'list_price'}</field>
+ </record>
+
+ <record id="account_move_view_form" model="ir.ui.view">
+ <field name="name">account.move.form.inherit.website_sale</field>
+ <field name="model">account.move</field>
+ <field name="inherit_id" ref="account.view_move_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//group[@name='sale_info_group']" position="inside">
+ <field name="website_id" groups="website.group_multi_website" attrs="{'invisible': [('website_id', '=', False)]}"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/website_sale/views/crm_team_views.xml b/addons/website_sale/views/crm_team_views.xml
new file mode 100644
index 00000000..f3070f10
--- /dev/null
+++ b/addons/website_sale/views/crm_team_views.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+ <record id="crm_team_salesteams_view_kanban_inherit_website_sale" model="ir.ui.view">
+ <field name="name">crm.team.kanban</field>
+ <field name="model">crm.team</field>
+ <field name="inherit_id" ref="sales_team.crm_team_salesteams_view_kanban"/>
+ <field name="arch" type="xml">
+ <data>
+ <xpath expr="//t[@name='third_options']" position="after">
+ <div class="row" t-if="record.abandoned_carts_count.raw_value">
+ <div class="col-8">
+ <div>
+ <a name="get_abandoned_carts" type="object">
+ <field name="abandoned_carts_count"/>
+ Abandoned Carts to Recover
+ </a>
+ </div>
+ </div>
+ <div class="col-4 text-right">
+ <field name="abandoned_carts_amount" widget="monetary"/>
+ </div>
+ </div>
+ </xpath>
+ </data>
+ </field>
+ </record>
+
+</odoo>
diff --git a/addons/website_sale/views/digest_views.xml b/addons/website_sale/views/digest_views.xml
new file mode 100644
index 00000000..08399e34
--- /dev/null
+++ b/addons/website_sale/views/digest_views.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="digest_digest_view_form" model="ir.ui.view">
+ <field name="name">digest.digest.view.form.inherit.website.sale.order</field>
+ <field name="model">digest.digest</field>
+ <field name="inherit_id" ref="digest.digest_digest_view_form" />
+ <field name="arch" type="xml">
+ <xpath expr="//group[@name='kpi_sales']" position="attributes">
+ <attribute name="string">Sales</attribute>
+ <attribute name="groups">sales_team.group_sale_salesman_all_leads</attribute>
+ </xpath>
+ <xpath expr="//group[@name='kpi_sales']" position="inside">
+ <field name="kpi_website_sale_total"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/website_sale/views/onboarding_views.xml b/addons/website_sale/views/onboarding_views.xml
new file mode 100644
index 00000000..5f57dffd
--- /dev/null
+++ b/addons/website_sale/views/onboarding_views.xml
@@ -0,0 +1,21 @@
+<odoo>
+ <!-- PAYMENT ACQUIRER -->
+ <template id="website_sale_onboarding_payment_acquirer_step" primary="True"
+ inherit_id="payment.onboarding_payment_acquirer_step">
+ <xpath expr="//t[@t-set='method']" position="replace">
+ <t t-set="method" t-value="'action_open_website_sale_onboarding_payment_acquirer'" />
+ </xpath>
+ <xpath expr="//t[@t-set='state']" position="replace">
+ <t t-set="state" t-value="state.get('website_sale_onboarding_payment_acquirer_state')" />
+ </xpath>
+ </template>
+
+ <record id="action_open_website_sale_onboarding_payment_acquirer_wizard" model="ir.actions.act_window">
+ <field name="name">Choose a payment method</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">website.sale.payment.acquirer.onboarding.wizard</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="payment.payment_acquirer_onboarding_wizard_form" />
+ <field name="target">new</field>
+ </record>
+</odoo>
diff --git a/addons/website_sale/views/product_views.xml b/addons/website_sale/views/product_views.xml
new file mode 100644
index 00000000..7df58ef2
--- /dev/null
+++ b/addons/website_sale/views/product_views.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="product_template_search_view_website" model="ir.ui.view">
+ <field name="name">product.template.search.published</field>
+ <field name="model">product.template</field>
+ <field name="inherit_id" ref="product.product_template_search_view"/>
+ <field name="arch" type="xml">
+ <xpath expr="//filter[@name='consumable']" position="after">
+ <separator/>
+ <filter string="Published" name="published" domain="[('is_published', '=', True)]"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="product_product_website_tree_view">
+ <field name="name">product.product.website.tree</field>
+ <field name="model">product.product</field>
+ <field name="inherit_id" ref="product.product_product_tree_view"/>
+ <field name="arch" type="xml">
+ <field name="name" position="after">
+ <field name="website_id" groups="website.group_multi_website" optional="show"/>
+ <field name="is_published" string="Is Published" optional="hide"/>
+ </field>
+ </field>
+ </record>
+
+ <!-- We want website_id to be shown outside of website module like other models -->
+ <record model="ir.ui.view" id="product_template_view_tree">
+ <field name="name">product.template.view.tree.inherit.website_sale</field>
+ <field name="model">product.template</field>
+ <field name="inherit_id" ref="product.product_template_tree_view"/>
+ <field name="arch" type="xml">
+ <field name="default_code" position="after">
+ <field name="website_id" groups="website.group_multi_website" optional="hide"/>
+ </field>
+ </field>
+ </record>
+
+ <!-- only website module template view should use the website_sequence -->
+ <record model="ir.ui.view" id="product_template_view_tree_website_sale">
+ <field name="name">product.template.view.tree.website_sale</field>
+ <field name="mode">primary</field>
+ <field name="model">product.template</field>
+ <field name="inherit_id" ref="website_sale.product_template_view_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="/tree" position="attributes">
+ <attribute name="default_order">website_sequence</attribute>
+ </xpath>
+ <field name="sequence" position="replace">
+ <field name="website_sequence" widget="handle"/>
+ </field>
+ <field name="website_id" position="after">
+ <field name="is_published" string="Is Published" optional="hide"/>
+ </field>
+ </field>
+ </record>
+
+ <record id="product_template_action_website" model="ir.actions.act_window">
+ <field name="name">Products</field>
+ <field name="res_model">product.template</field>
+ <field name="view_mode">kanban,tree,form,activity</field>
+ <field name="view_id" ref="product_template_view_tree_website_sale"/>
+ <field name="search_view_id" ref="product_template_search_view_website"/>
+ <field name="context">{'search_default_published': 1}</field>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">
+ Create a new product
+ </p><p>
+ A product can be either a physical product or a service that you sell to your customers.
+ </p>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="product_template_form_view_invoice_policy">
+ <field name="name">product.template.invoice.policy</field>
+ <field name="model">product.template</field>
+ <field name="inherit_id" ref="sale.product_template_form_view_invoice_policy"/>
+ <field name="arch" type="xml">
+ <xpath expr="//group[@name='invoicing']" position="attributes">
+ <attribute name="invisible">0</attribute>
+ </xpath>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="product_template_form_view">
+ <field name="name">product.template.product.website.form</field>
+ <field name="model">product.template</field>
+ <field name="inherit_id" ref="product.product_template_form_view"/>
+ <field name="arch" type="xml">
+ <!-- add state field in header -->
+ <div name="button_box" position="inside">
+ <field name="is_published" widget="website_redirect_button" attrs="{'invisible': [('sale_ok','=',False)]}"/>
+ </div>
+ <xpath expr="//page[@name='sales']" position="after">
+ <page name="shop" string="eCommerce" attrs="{'invisible': [('sale_ok','=',False)]}">
+ <group name="shop">
+ <group string="Shop">
+ <field name="website_url" invisible="1"/>
+ <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
+ <field name="website_sequence" groups="base.group_no_one"/>
+ <field name="public_categ_ids" widget="many2many_tags" string="Categories"/>
+ <field name="alternative_product_ids" widget="many2many_tags" domain="[('id', '!=', active_id), '|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
+ <field name="accessory_product_ids" widget="many2many_tags"/>
+ <field name="website_ribbon_id" groups="base.group_no_one"/>
+ </group>
+ </group>
+ <group name="product_template_images" string="Extra Product Media">
+ <field name="product_template_image_ids" class="o_website_sale_image_list" context="{'default_name': name}" mode="kanban" options="{'create_text':'Add a Media'}" nolabel="1"/>
+ </group>
+ </page>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="product_product_view_form_easy_inherit_website_sale" model="ir.ui.view">
+ <field name="name">product.product.view.form.easy.inherit.website_sale</field>
+ <field name="model">product.product</field>
+ <field name="inherit_id" ref="product.product_variant_easy_edit_view"/>
+ <field name="arch" type="xml">
+ <sheet position="inside">
+ <group name="product_variant_images" string="Extra Variant Media">
+ <field name="product_variant_image_ids" class="o_website_sale_image_list" context="{'default_name': name}" mode="kanban" options="{'create_text':'Add a Media'}" nolabel="1"/>
+ </group>
+ </sheet>
+ </field>
+ </record>
+
+ <!-- Product Public Categories -->
+ <record id="product_public_category_form_view" model="ir.ui.view">
+ <field name="name">product.public.category.form</field>
+ <field name="model">product.public.category</field>
+ <field name="arch" type="xml">
+ <form string="Website Public Categories">
+ <sheet>
+ <field name="image_1920" widget="image" class="oe_avatar" options="{'preview_image': 'image_128'}"/>
+ <div class="oe_left">
+ <group>
+ <field name="name"/>
+ <field name="parent_id"/>
+ <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
+ <field name="sequence" groups="base.group_no_one"/>
+ </group>
+ </div>
+ </sheet>
+ </form>
+ </field>
+ </record>
+ <record id="product_public_category_tree_view" model="ir.ui.view">
+ <field name="name">product.public.category.tree</field>
+ <field name="model">product.public.category</field>
+ <field name="field_parent" eval="False"/>
+ <field name="arch" type="xml">
+ <tree string="Product Public Categories">
+ <field name="sequence" widget="handle"/>
+ <field name="display_name"/>
+ <field name="website_id" groups="website.group_multi_website"/>
+ </tree>
+ </field>
+ </record>
+ <record id="product_public_category_action" model="ir.actions.act_window">
+ <field name="name">eCommerce Categories</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">product.public.category</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" eval="False"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">
+ Define a new category
+ </p><p>
+ Categories are used to browse your products through the
+ touchscreen interface.
+ </p>
+ </field>
+ </record>
+
+ <record id="website_sale_pricelist_form_view" model="ir.ui.view">
+ <field name="name">website_sale.pricelist.form</field>
+ <field name="inherit_id" ref="product.product_pricelist_view" />
+ <field name="model">product.pricelist</field>
+ <field name="arch" type="xml">
+ <xpath expr="//group[@name='pricelist_availability']" position="after">
+ <group name="pricelist_website" string="Website">
+ <field name="website_id" options="{'no_create': True}"/>
+ <field name="selectable"/>
+ <field name="code"/>
+ </group>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="website_sale_pricelist_tree_view" model="ir.ui.view">
+ <field name="name">product.pricelist.tree.inherit.product</field>
+ <field name="model">product.pricelist</field>
+ <field name="inherit_id" ref="product.product_pricelist_view_tree"/>
+ <field name="arch" type="xml">
+ <field name="currency_id" position="after">
+ <field name="selectable" />
+ <field name="website_id" groups="website.group_multi_website"/>
+ </field>
+ </field>
+ </record>
+
+ <!-- This view should only be used from the product o2m because the required field product_tmpl_id has to be automatically set. -->
+ <record id="view_product_image_form" model="ir.ui.view">
+ <field name="name">product.image.view.form</field>
+ <field name="model">product.image</field>
+ <field name="arch" type="xml">
+ <form string="Product Images">
+ <field name="sequence" invisible="1"/>
+ <div class="row o_website_sale_image_modal">
+ <div class="col-md-6 col-xl-5">
+ <label for="name" string="Image Name"/>
+ <h2><field name="name" placeholder="Image Name"/></h2>
+ <label for="video_url" string="Video URL"/><br/>
+ <field name="video_url"/><br/>
+ </div>
+ <div class="col-md-6 col-xl-7 text-center o_website_sale_image_modal_container">
+ <div class="row">
+ <div class="col">
+ <field name="image_1920" widget="image"/>
+ </div>
+ <div class="col" attrs="{'invisible': [('video_url', 'in', ['', False])]}">
+ <div class="o_video_container p-2">
+ <span>Video Preview</span>
+ <field name="embed_code" class="mt-2" widget="video_preview"/>
+ <h4 class="o_invalid_warning text-muted text-center" attrs="{'invisible': [('embed_code', '!=', False)]}">
+ Please enter a valid Video URL.
+ </h4>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+ </field>
+ </record>
+ <record id="product_image_view_kanban" model="ir.ui.view">
+ <field name="name">product.image.view.kanban</field>
+ <field name="model">product.image</field>
+ <field name="arch" type="xml">
+ <kanban string="Product Images" default_order="sequence">
+ <field name="id"/>
+ <field name="name"/>
+ <field name="image_1920"/>
+ <field name="sequence" widget="handle"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div class="card oe_kanban_global_click p-0">
+ <div class="o_squared_image">
+ <img class="card-img-top" t-att-src="kanban_image('product.image', 'image_1920', record.id.raw_value)" t-att-alt="record.name.value"/>
+ </div>
+ <div class="card-body p-0">
+ <h4 class="card-title p-2 m-0 bg-200">
+ <small><field name="name"/></small>
+ </h4>
+ </div>
+ <!-- below 100 Kb: good -->
+ <t t-if="record.image_1920.raw_value.length &lt; 100*1000">
+ <t t-set="size_status" t-value="'badge-success'"/>
+ <t t-set="message">Acceptable file size</t>
+ </t>
+ <!-- below 1000 Kb: decent -->
+ <t t-elif="record.image_1920.raw_value.length &lt; 1000*1000">
+ <t t-set="size_status" t-value="'badge-warning'" />
+ <t t-set="message">Huge file size. The image should be optimized/reduced.</t>
+ </t>
+ <!-- above 1000 Kb: bad -->
+ <t t-else="1">
+ <t t-set="size_status" t-value="'badge-danger'"/>
+ <t t-set="message">Optimization required! Reduce the image size or increase your compression settings.</t>
+ </t>
+ <span t-attf-class="badge #{size_status} o_product_image_size" t-esc="record.image_1920.value" t-att-title="message"/>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+</odoo>
diff --git a/addons/website_sale/views/res_config_settings_views.xml b/addons/website_sale/views/res_config_settings_views.xml
new file mode 100644
index 00000000..a00e5e4d
--- /dev/null
+++ b/addons/website_sale/views/res_config_settings_views.xml
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+ <record id="res_config_settings_view_form" model="ir.ui.view">
+ <field name="name">res.config.settings.view.form.inherit.website.sale</field>
+ <field name="model">res.config.settings</field>
+ <field name="inherit_id" ref="website.res_config_settings_view_form"/>
+ <field name="arch" type="xml">
+ <div id="webmaster_settings" position="after">
+ <h2>Products</h2>
+ <div class="row mt16 o_settings_container" id="sale_product_catalog_settings">
+ <div class="col-12 col-lg-6 o_setting_box" id="product_attributes_setting">
+ <div class="o_setting_left_pane">
+ <field name="group_product_variant"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="group_product_variant"/>
+ <div class="text-muted">
+ Sell variants of a product using attributes (size, color, etc.)
+ </div>
+ <div class="content-group" attrs="{'invisible': [('group_product_variant', '=', False)]}">
+ <div class="mt8">
+ <button type="action" name="%(product.attribute_action)d" string="Attributes" class="btn-link" icon="fa-arrow-right"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="prompt_optional_products">
+ <div class="o_setting_left_pane">
+ <field name="module_sale_product_configurator" string="Optional Products"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="module_sale_product_configurator" string="Optional Products"/>
+ <div class="text-muted">
+ Display a prompt with optional products when adding to cart
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="digital_content_setting" title="Provide customers with product-specific links or downloadable content in the confirmation page of the checkout process if the payment gets through. To do so, attach some files to a product using the new Files button and publish them.">
+ <div class="o_setting_left_pane">
+ <field name="module_website_sale_digital"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="module_website_sale_digital"/>
+ <div class="text-muted">
+ Sell content to download or URL links
+ </div>
+ </div>
+ </div>
+
+ <div class="col-12 col-lg-6 o_setting_box" id="wishlist_option_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_website_sale_wishlist"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="module_website_sale_wishlist"/>
+ <div class="text-muted">
+ Let returning shoppers save products in a wishlist
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="comparator_option_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_website_sale_comparison"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="module_website_sale_comparison"/>
+ <div class="text-muted">
+ Allow shoppers to compare products based on their attributes
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="product_availability_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_website_sale_stock"/>
+ </div>
+ <div class="o_setting_right_pane" name="stock_inventory_availability">
+ <label for="module_website_sale_stock"/>
+ <div class="text-muted">
+ Manage availability of products
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <h2>Pricing</h2>
+ <div class="row mt16 o_settings_container" id="sale_pricing_settings">
+ <div class="col-12 col-lg-6 o_setting_box" id="website_tax_inclusion_setting">
+ <div class="o_setting_right_pane">
+ <label string="Product Prices" for="show_line_subtotals_tax_selection"/>
+ <div class="text-muted">
+ Product prices displaying in web catalog
+ </div>
+ <div class="content-group">
+ <div class="mt16">
+ <field name="show_line_subtotals_tax_selection" class="o_light_label" widget="radio"/>
+ <field name="group_show_line_subtotals_tax_included" invisible="1"/>
+ <field name="group_show_line_subtotals_tax_excluded" invisible="1"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="pricelists_setting" title="With the first mode you can set several prices in the product config form (from Sales tab). With the second one, you set prices and computation rules from Pricelists.">
+ <div class="o_setting_left_pane">
+ <field name="group_product_pricelist"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="group_product_pricelist"/>
+ <div class="text-muted">
+ Apply specific prices per country, discounts, etc.
+ </div>
+ <div class="content-group mt16" attrs="{'invisible': [('group_product_pricelist', '=', False)]}">
+ <field name="group_sale_pricelist" invisible="1"/>
+ <field name="group_product_pricelist" invisible="1"/>
+ <field name="product_pricelist_setting" class="o_light_label" widget="radio"/>
+ </div>
+ <div class="mt8" attrs="{'invisible': [('group_product_pricelist', '=', False)]}">
+ <button type="action" name="%(product.product_pricelist_action2)d" string="Pricelists" class="btn-link" icon="fa-arrow-right"/>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="discount_setting" title="Apply manual discounts on sales order lines or display discounts computed from pricelists (option to activate in the pricelist configuration).">
+ <div class="o_setting_left_pane">
+ <field name="group_discount_per_so_line"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="group_discount_per_so_line"/>
+ <div class="text-muted">
+ Grant discounts on sales order lines
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="multicurrencies_setting" title="This adds the choice of a currency on pricelists.">
+ <div class="o_setting_left_pane">
+ <field name="group_multi_currency"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="group_multi_currency"/>
+ <div class="text-muted">
+ Sell in several currencies
+ </div>
+ <div class="content-group" attrs="{'invisible': [('group_multi_currency', '=', False)]}">
+ <div class="mt8">
+ <button type="action" name="%(base.action_currency_form)d" string="Currencies" class="btn-link" icon="fa-arrow-right"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box"
+ id="promotion_coupon_programs"
+ title="Boost your sales with two kinds of discount programs: promotions and coupon codes. Specific conditions can be set (products, customers, minimum purchase amount, period). Rewards can be discounts (% or amount) or free products.">
+ <div class="o_setting_left_pane">
+ <field name="module_sale_coupon" />
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="module_sale_coupon"/>
+ <div class="text-muted" id="website_sale_coupon">
+ Manage promotion &amp; coupon programs
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <h2>Shipping</h2>
+ <div class="row mt16 o_settings_container" id="sale_shipping_settings">
+ <div class="col-12 col-lg-6 o_setting_box" id="shipping_address_setting">
+ <div class="o_setting_left_pane">
+ <field name="group_delivery_invoice_address"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="group_delivery_invoice_address"/>
+ <div class="text-muted">
+ Let the customer enter a shipping address
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="delivery_method_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_website_sale_delivery"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="Shipping Costs" for="module_website_sale_delivery"/>
+ <div class="text-muted" id="msg_delivery_method_setting">
+ Compute shipping costs on orders
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="ups_provider_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_delivery_ups" widget="upgrade_boolean"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="UPS" for="module_delivery_ups"/>
+ <div class="text-muted" id="website_delivery_ups">
+ Compute shipping costs and ship with UPS
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="shipping_provider_dhl_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_delivery_dhl" widget="upgrade_boolean"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="DHL" for="module_delivery_dhl"/>
+ <div class="text-muted" id="website_delivery_dhl">
+ Compute shipping costs and ship with DHL
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="shipping_provider_fedex_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_delivery_fedex" widget="upgrade_boolean"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="FedEx" for="module_delivery_fedex"/>
+ <div class="text-muted" id="website_delivery_fedex">
+ Compute shipping costs and ship with FedEx
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="shipping_provider_usps_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_delivery_usps" widget="upgrade_boolean"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="USPS" for="module_delivery_usps"/>
+ <div class="text-muted" id="website_delivery_usps">
+ Compute shipping costs and ship with USPS
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="shipping_provider_bpost_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_delivery_bpost" widget="upgrade_boolean"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="bpost" for="module_delivery_bpost"/>
+ <div class="text-muted" id="website_delivery_bpost">
+ Compute shipping costs and ship with bpost
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="shipping_provider_easypost_setting">
+ <div class="o_setting_left_pane">
+ <field name="module_delivery_easypost" widget="upgrade_boolean"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label string="Easypost" for="module_delivery_easypost"/>
+ <div class="text-muted" id="website_delivery_easypost">
+ Compute shipping cost and ship with Easypost
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <field name='module_account' invisible="1"/>
+ <div attrs="{'invisible': [('module_account', '=', False)]}">
+ <h2>Invoicing</h2>
+ <div class="row mt16 o_settings_container" id="sale_invoicing_settings">
+ <div class="col-12 col-lg-6 o_setting_box" id="invoicing_policy_setting" title="The mode selected here applies as invoicing policy of any new product created but not of products already existing.">
+ <div class="o_setting_right_pane">
+ <span class="o_form_label">Invoicing Policy</span>
+ <div class="text-muted">
+ Issue invoices to customers
+ </div>
+ <div class="content-group">
+ <div class="mt16">
+ <field name="default_invoice_policy" class="o_light_label" widget="radio"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box"
+ id="automatic_invoice_generation"
+ attrs="{'invisible': [('default_invoice_policy', '=', 'delivery')]}">
+ <div class="o_setting_left_pane">
+ <field name="automatic_invoice" nolabel="1"/>
+ </div>
+ <div class="o_setting_right_pane">
+ <label for="automatic_invoice"/>
+ <div class="text-muted">
+ Generate the invoice automatically when the online payment is confirmed
+ </div>
+ <div attrs="{'invisible': [('automatic_invoice','=',False)]}">
+ <label for="template_id" class="o_light_label"/>
+ <field name="template_id" class="oe_inline"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <h2>Orders Followup</h2>
+ <div class="row mt16 o_settings_container" id="sale_checkout_settings">
+ <div class="col-12 col-lg-6 o_setting_box" id="checkout_assignation_setting">
+ <div class="o_setting_right_pane">
+ <span class="o_form_label">Assignment</span>
+ <span class="fa fa-lg fa-globe" title="Values set here are website-specific." groups="website.group_multi_website"/>
+ <div class="text-muted">
+ Assignment of online orders
+ </div>
+ <div class="content-group">
+ <div class="row mt16">
+ <label class="o_light_label col-lg-3" string="Sales Team" for="salesteam_id"/>
+ <field name="salesteam_id" kanban_view_ref="%(sales_team.crm_team_view_kanban)s" />
+ </div>
+ <div class="row">
+ <label class="o_light_label col-lg-3" for="salesperson_id"/>
+ <field name="salesperson_id"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-lg-6 o_setting_box" id="confirmation_email_setting">
+ <div class="o_setting_right_pane">
+ <span class="o_form_label">Confirmation Email</span>
+ <div class="text-muted">
+ Email sent to the customer after the checkout
+ </div>
+ <div class="row mt16">
+ <label for="confirmation_template_id" string="Email Template" class="col-lg-4 o_light_label"/>
+ <field name="confirmation_template_id" class="oe_inline"/>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-lg-6 o_setting_box" id="abandoned_carts_setting" title="Abandoned carts are all carts left unconfirmed by website visitors. You can find them in *Website > Orders > Abandoned Carts*. From there you can send recovery emails to visitors who entered their contact details.">
+ <div class="o_setting_left_pane"/>
+ <div class="o_setting_right_pane">
+ <span class="o_form_label">Abandoned Carts</span>
+ <span class="fa fa-lg fa-globe" title="Values set here are website-specific." groups="website.group_multi_website"/>
+ <div class="text-muted">
+ Send a recovery email when a cart is abandoned
+ </div>
+ <div class="content-group" title="This email template is suggested by default when you send a recovery email.">
+ <div class="row mt16">
+ <label for="cart_recovery_mail_template" string="Email Template" class="col-lg-4 o_light_label"/>
+ <field name="cart_recovery_mail_template" class="oe_inline"/>
+ </div>
+ </div>
+ <div class="content-group" title="Carts are flagged as abandoned after this delay.">
+ <div class="row mt16">
+ <div class="col-12">
+ <label for="cart_abandoned_delay" string="Cart is abandoned after" class="o_light_label"/>
+ <field class="col-2" name="cart_abandoned_delay"/> hours.
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/website_sale/views/sale_order_views.xml b/addons/website_sale/views/sale_order_views.xml
new file mode 100644
index 00000000..36f9e226
--- /dev/null
+++ b/addons/website_sale/views/sale_order_views.xml
@@ -0,0 +1,298 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="view_sales_order_filter_ecommerce" model="ir.ui.view">
+ <field name="name">sale.order.ecommerce.search.view</field>
+ <field name="model">sale.order</field>
+ <field name="inherit_id" ref="sale.view_sales_order_filter"/>
+ <field name="mode">primary</field>
+ <field name="arch" type="xml">
+ <xpath expr="//filter[@name='my_sale_orders_filter']" position="before">
+ <filter string="Confirmed" name="order_confirmed" domain="[('state', 'in', ('sale', 'done'))]"/>
+ <filter string="Unpaid" name="order_unpaid" domain="[('state', '=', 'sent'), ('website_id', '!=', False)]"/>
+ <filter string="Abandoned" name="order_abandoned" domain="[('is_abandoned_cart', '=', True)]"/>
+ <separator/>
+ <filter string="Order Date" name="order_date" date="date_order"/>
+ <separator/>
+ <filter string="From Website" name="from_website" domain="[('website_id', '!=', False)]"/>
+ <separator/>
+ <!-- Dashboard filter - used by context -->
+ <filter string="Last Week" invisible="1" name="week" domain="[('date_order','&gt;', (context_today() - datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"/>
+ <filter string="Last Month" invisible="1" name="month" domain="[('date_order','&gt;', (context_today() - datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"/>
+ <filter string="Last Year" invisible="1" name="year" domain="[('date_order','&gt;', (context_today() - datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="view_sales_order_filter_ecommerce_unpaid" model="ir.ui.view">
+ <field name="name">sale.order.ecommerce.search.unpaid.view</field>
+ <field name="model">sale.order</field>
+ <field name="inherit_id" ref="sale.view_sales_order_filter"/>
+ <field name="mode">primary</field>
+ <field name="priority">32</field>
+ <field name="arch" type="xml">
+ <xpath expr="//filter[@name='my_sale_orders_filter']" position="attributes">
+ <attribute name="invisible">1</attribute>
+ </xpath>
+ <xpath expr="//filter[@name='my_sale_orders_filter']" position="before">
+ <filter string="Order Date" name="order_date" date="date_order"/>
+ <separator/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="sale_order_view_form_cart_recovery" model="ir.ui.view">
+ <field name="name">sale.order.form.abandoned.cart</field>
+ <field name="model">sale.order</field>
+ <field name="inherit_id" ref="sale.view_order_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='team_id']" position="after">
+ <field name="is_abandoned_cart" invisible="1"/>
+ <field name="cart_recovery_email_sent" invisible="1"/>
+ </xpath>
+ <xpath expr="//button[@name='action_quotation_send' and @states='draft']" position="attributes">
+ <!-- The '| and the '&amp' opertors are necessary because draft state of the parent concatenate the domain -->
+ <attribute name="attrs">{'invisible': ['|','&amp;',('is_abandoned_cart', '=', True), ('cart_recovery_email_sent', '=', False)]}</attribute>
+ </xpath>
+ <xpath expr="//button[@name='action_quotation_send']" position="after">
+ <button name="action_recovery_email_send"
+ string="Send a Recovery Email"
+ type="object"
+ class="btn-primary"
+ attrs="{'invisible': ['|', ('is_abandoned_cart', '=', False), ('cart_recovery_email_sent', '=', True)]}"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="action_orders_ecommerce" model="ir.actions.act_window">
+ <field name="name">Orders</field>
+ <field name="res_model">sale.order</field>
+ <field name="view_mode">tree,form,kanban,activity</field>
+ <field name="domain">[]</field>
+ <field name="context">{'show_sale': True, 'search_default_order_confirmed': 1, 'search_default_from_website': 1}</field>
+ <field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ There is no confirmed order from the website
+ </p>
+ </field>
+ </record>
+
+ <!-- Dashboard Action -->
+ <record id="action_unpaid_orders_ecommerce" model="ir.actions.act_window">
+ <field name="name">Unpaid Orders</field>
+ <field name="res_model">sale.order</field>
+ <field name="view_mode">tree,form,kanban,activity</field>
+ <field name="domain">[('state', '=', 'sent'), ('website_id', '!=', False)]</field>
+ <field name="context">{'show_sale': True, 'create': False}</field>
+ <field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ There is no unpaid order from the website yet
+ </p><p>
+ Process the order once the payment is received.
+ </p>
+ </field>
+ </record>
+
+ <record id="view_sales_order_filter_ecommerce_abondand" model="ir.ui.view">
+ <field name="name">sale.order.ecommerce.abondand.view</field>
+ <field name="model">sale.order</field>
+ <field name="priority">32</field>
+ <field name="arch" type="xml">
+ <search string="Search Abandoned Sales Orders">
+ <field name="name"/>
+ <filter string="Creation Date" name="creation_date" date="create_date"/>
+ <separator/>
+ <filter string="Recovery Email to Send" name="recovery_email" domain="[('cart_recovery_email_sent', '=', False)]" />
+ <filter string="Recovery Email Sent" name="recovery_email_set" domain="[('cart_recovery_email_sent', '=', True)]" />
+ <group expand="0" string="Group By">
+ <filter string="Order Date" name="order_date" domain="[]" context="{'group_by':'date_order'}"/>
+ </group>
+ <!-- Dashboard filter - used by context -->
+ <filter string="Last Week" invisible="1" name="week" domain="[('date_order','&gt;', (context_today() - datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"/>
+ <filter string="Last Month" invisible="1" name="month" domain="[('date_order','&gt;', (context_today() - datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"/>
+ <filter string="Last Year" invisible="1" name="year" domain="[('date_order','&gt;', (context_today() - datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"/>
+ </search>
+ </field>
+ </record>
+
+ <!-- Dashboard Action -->
+ <record id="sale_order_action_to_invoice" model="ir.actions.act_window">
+ <field name="name">Orders To Invoice</field>
+ <field name="res_model">sale.order</field>
+ <field name="view_mode">tree,form,kanban</field>
+ <field name="domain">[('state', 'in', ('sale', 'done')), ('order_line', '!=', False), ('invoice_status', '=', 'to invoice'), ('website_id', '!=', False)]</field>
+ <field name="context">{'show_sale': True, 'search_default_order_confirmed': 1, 'create': False}</field>
+ <field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ You don't have any order to invoice from the website
+ </p>
+ </field>
+ </record>
+
+ <!-- Server action to send multiple recovery email-->
+ <record id="ir_actions_server_sale_cart_recovery_email" model="ir.actions.server">
+ <field name="name">Send a Cart Recovery Email</field>
+ <field name="type">ir.actions.server</field>
+ <field name="model_id" ref="model_sale_order"/>
+ <field name="state">code</field>
+ <field name="code">
+ if records:
+ action = records.action_recovery_email_send()
+ </field>
+ <field name="binding_model_id" ref="sale.model_sale_order"/>
+ <field name="binding_view_types">list,form</field>
+ </record>
+
+ <record id="action_view_unpaid_quotation_tree" model="ir.actions.act_window">
+ <field name="name">Unpaid Orders</field>
+ <field name="res_model">sale.order</field>
+ <field name="view_mode">tree,kanban,form,activity</field>
+ <field name="domain">[('state', '=', 'sent'), ('website_id', '!=', False)]</field>
+ <field name="context" eval="{'show_sale': True, 'create': False}"/>
+ <field name="view_id" ref="sale.view_quotation_tree"/>
+ <field name="search_view_id" ref="view_sales_order_filter_ecommerce_unpaid"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ There is no unpaid order from the website yet
+ </p><p>
+ Process the order once the payment is received.
+ </p>
+ </field>
+ </record>
+
+ <record id="action_view_abandoned_tree" model="ir.actions.act_window">
+ <field name="name">Abandoned Carts</field>
+ <field name="res_model">sale.order</field>
+ <field name="view_mode">tree,kanban,form,activity</field>
+ <field name="domain">[('is_abandoned_cart', '=', 1)]</field>
+ <field name="context" eval="{'show_sale': True, 'create': False, 'public_partner_id': ref('base.public_partner'), 'search_default_recovery_email': True}"/>
+ <field name="view_id" ref="sale.view_quotation_tree"/>
+ <field name="search_view_id" ref="view_sales_order_filter_ecommerce_abondand"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ No abandoned carts found
+ </p><p>
+ You'll find here all the carts abandoned by your visitors.
+ If they completed their address, you should send them a recovery email!
+ </p><p>
+ The time to mark a cart as abandoned can be changed in the settings.
+ </p>
+ </field>
+ </record>
+
+ <!-- Main website sale menu items -->
+ <menuitem id="menu_orders" name="Orders"
+ parent="website.menu_website_configuration" sequence="2"
+ groups="sales_team.group_sale_salesman"/>
+ <menuitem id="menu_catalog" name="Products"
+ parent="website.menu_website_configuration" sequence="3"
+ groups="sales_team.group_sale_salesman"/>
+ <menuitem id="menu_reporting" name="Reporting"
+ parent="website.menu_website_configuration" sequence="99"
+ groups="sales_team.group_sale_manager"/>
+ <menuitem id="website.menu_website_global_configuration" name="Configuration"
+ parent="website.menu_website_configuration" sequence="100"
+ groups="base.group_system,sales_team.group_sale_manager"/>
+
+ <menuitem id="menu_ecommerce_settings" name="eCommerce" sequence="50"
+ parent="website.menu_website_global_configuration"/>
+ <menuitem id="menu_product_settings" name="Products" sequence="80"
+ parent="website.menu_website_global_configuration"/>
+
+ <!-- Orders sub-menus -->
+ <menuitem id="menu_orders_orders" name="Orders"
+ action="action_orders_ecommerce"
+ parent="menu_orders" sequence="1"/>
+ <menuitem id="menu_orders_unpaid_orders" name="Unpaid Orders"
+ action="action_view_unpaid_quotation_tree"
+ parent="menu_orders" sequence="2"/>
+ <menuitem id="menu_orders_abandoned_orders" name="Abandoned Carts"
+ action="action_view_abandoned_tree"
+ parent="menu_orders" sequence="3"/>
+ <menuitem id="menu_orders_customers" name="Customers"
+ action="base.action_partner_customer_form"
+ parent="menu_orders" sequence="4"/>
+
+
+ <!-- Catalog sub-menus -->
+ <menuitem id="menu_catalog_products" name="Products"
+ action="product_template_action_website"
+ parent="menu_catalog" sequence="1"/>
+ <menuitem id="product_catalog_variants" name="Product Variants"
+ action="product.product_normal_action"
+ parent="menu_catalog" groups="product.group_product_variant" sequence="2"/>
+ <menuitem id="menu_catalog_pricelists" name="Pricelists"
+ action="product.product_pricelist_action2"
+ parent="menu_catalog" groups="product.group_product_pricelist" sequence="4"/>
+
+ <!-- Reporting sub-menus -->
+ <menuitem id="menu_report_sales" name="Online Sales"
+ action="sale_report_action_dashboard"
+ parent="menu_reporting" sequence="1"/>
+
+ <!-- Configuration sub-menus -->
+ <menuitem id="menu_ecommerce_payment_acquirers"
+ action="payment.action_payment_acquirer"
+ parent="menu_ecommerce_settings" name="Payment Acquirers"/>
+ <menuitem id="menu_ecommerce_payment_tokens"
+ action="payment.payment_token_action"
+ groups="base.group_no_one"
+ parent="menu_ecommerce_settings"/>
+ <menuitem id="menu_ecommerce_payment_icons"
+ action="payment.action_payment_icon"
+ groups="base.group_no_one"
+ parent="menu_ecommerce_settings"/>
+ <menuitem id="menu_ecommerce_payment_transactions"
+ action="payment.action_payment_transaction"
+ groups="base.group_no_one"
+ parent="menu_ecommerce_settings"/>
+ <menuitem id="menu_catalog_categories"
+ action="product_public_category_action"
+ parent="menu_product_settings" sequence="1"/>
+ <menuitem id="menu_product_attribute_action"
+ action="product.attribute_action"
+ parent="menu_product_settings" groups="product.group_product_variant" sequence="2"/>
+
+ <record id="sale_order_view_form" model="ir.ui.view">
+ <field name="name">sale.order.form</field>
+ <field name="model">sale.order</field>
+ <field name="inherit_id" ref="sale.view_order_form"/>
+ <field name="arch" type="xml">
+ <field name="partner_id" position="attributes">
+ <attribute name="context">{
+ 'display_website': True,
+ 'res_partner_search_mode': 'customer',
+ 'show_address': 1,
+ 'show_vat': True,
+ }</attribute>
+ </field>
+ <field name="team_id" position="after">
+ <field name="website_id" attrs="{'invisible': [('website_id', '=', False)]}" groups="website.group_multi_website"/>
+ </field>
+ </field>
+ </record>
+
+ <record id="view_quotation_tree" model="ir.ui.view">
+ <field name="name">sale.order.tree.inherit.website.sale</field>
+ <field name="model">sale.order</field>
+ <field name="inherit_id" ref="sale.view_quotation_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='user_id']" position="before">
+ <field name="website_id" optional="hide"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="view_order_tree" model="ir.ui.view">
+ <field name="name">sale.order.tree.inherit.website.sale</field>
+ <field name="model">sale.order</field>
+ <field name="inherit_id" ref="sale.view_order_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='partner_id']" position="before">
+ <field name="website_id" optional="show"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/website_sale/views/sale_report_views.xml b/addons/website_sale/views/sale_report_views.xml
new file mode 100644
index 00000000..27d3b724
--- /dev/null
+++ b/addons/website_sale/views/sale_report_views.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="sale_report_view_search_website" model="ir.ui.view">
+ <field name="name">sale.report.search</field>
+ <field name="model">sale.report</field>
+ <field name="arch" type="xml">
+ <search string="Sales">
+ <field name="website_id" groups="website.group_multi_website"/>
+ <field name="product_id"/>
+ <field name="categ_id"/>
+ <field name="partner_id"/>
+ <field name="country_id"/>
+ <field name="company_id" groups="base.group_multi_company"/>
+ <filter string="Confirmed Orders" name="confirmed" domain="[('state', 'in', ['sale', 'done'])]"/>
+ <separator/>
+ <filter name="filter_date" date="date" default_period="this_month"/>
+ <group expand="0" string="Group By">
+ <filter string="Website" name="groupby_website" context="{'group_by':'website_id'}" groups="website.group_multi_website"/>
+ <filter string="Product" name="groupby_product" context="{'group_by':'product_id'}"/>
+ <filter string="Product Category" name="groupby_product_category" context="{'group_by':'categ_id'}"/>
+ <filter string="Customer" name="groupby_customer" context="{'group_by':'partner_id'}"/>
+ <filter string="Customer Country" name="groupby_country" context="{'group_by':'country_id'}"/>
+ <filter string="Status" name="groupby_status" context="{'group_by':'state'}"/>
+ <separator orientation="vertical"/>
+ <filter string="Order Date" name="groupby_order_date" context="{'group_by':'date'}"/>
+ <!-- Dashboard filter - used by context -->
+ <filter string="Last Week" invisible="1" name="week" domain="[('date','&gt;=', (context_today() - datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"/>
+ <filter string="Last Month" invisible="1" name="month" domain="[('date','&gt;=', (context_today() - datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"/>
+ <filter string="Last Year" invisible="1" name="year" domain="[('date','&gt;=', (context_today() - datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record id="sale_report_view_pivot_website" model="ir.ui.view">
+ <field name="name">sale.report.view.pivot.website</field>
+ <field name="model">sale.report</field>
+ <field name="arch" type="xml">
+ <pivot string="Sales Report" disable_linking="True" sample="1">
+ <field name="date" type="row"/>
+ <field name="state" type="col"/>
+ <field name="price_subtotal" type="measure"/>
+ </pivot>
+ </field>
+ </record>
+
+ <record id="sale_report_view_graph_website" model="ir.ui.view">
+ <field name="name">sale.report.view.graph.website</field>
+ <field name="model">sale.report</field>
+ <field name="arch" type="xml">
+ <graph string="Sale Report" type="bar" sample="1" disable_linking="1">
+ <field name="date"/>
+ <field name="price_subtotal" type='measure'/>
+ </graph>
+ </field>
+ </record>
+
+ <record id="sale_report_action_dashboard" model="ir.actions.act_window">
+ <field name="name">Online Sales Analysis</field>
+ <field name="res_model">sale.report</field>
+ <field name="view_mode">pivot,graph</field>
+ <field name="domain">[('state','in',('sale', 'done')), ('website_id', '!=', False)]</field>
+ <field name="search_view_id" ref="sale_report_view_search_website"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ You don't have any order from the website
+ </p>
+ </field>
+ </record>
+
+ <record id="sale_report_action_view_pivot_website" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">pivot</field>
+ <field name="view_id" ref="sale_report_view_pivot_website"/>
+ <field name="act_window_id" ref="sale_report_action_dashboard"/>
+ </record>
+
+ <record id="sale_report_action_view_graph_website" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">graph</field>
+ <field name="view_id" ref="sale_report_view_graph_website"/>
+ <field name="act_window_id" ref="sale_report_action_dashboard"/>
+ </record>
+
+ <record id="sale_report_action_carts" model="ir.actions.act_window">
+ <field name="name">Sales</field>
+ <field name="res_model">sale.report</field>
+ <field name="view_mode">pivot,graph</field>
+ <field name="domain">[('website_id', '!=', False)]</field>
+ <field name="search_view_id" ref="sale_report_view_search_website"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ You don't have any order from the website
+ </p>
+ </field>
+ </record>
+
+ <record id="sale_report_action_view_pivot_carts" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">pivot</field>
+ <field name="view_id" ref="sale_report_view_pivot_website"/>
+ <field name="act_window_id" ref="sale_report_action_carts"/>
+ </record>
+
+ <record id="sale_report_action_view_graph_carts" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">graph</field>
+ <field name="view_id" ref="sale_report_view_graph_website"/>
+ <field name="act_window_id" ref="sale_report_action_carts"/>
+ </record>
+</odoo>
diff --git a/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml b/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml
new file mode 100644
index 00000000..2e871086
--- /dev/null
+++ b/addons/website_sale/views/snippets/s_dynamic_snippet_products.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <template id="s_dynamic_snippet_products" name="Dynamic Products">
+ <t t-call="website.s_dynamic_snippet_template">
+ <t t-set="snippet_name" t-value="'s_dynamic_snippet_products'"/>
+ </t>
+ </template>
+ <template id="s_dynamic_snippet_products_options" inherit_id="website.snippet_options">
+ <xpath expr="." position="inside">
+ <t t-call="website.dynamic_snippet_carousel_options_template">
+ <t t-set="snippet_name" t-value="'dynamic_snippet_products'"/>
+ <t t-set="snippet_selector" t-value="'.s_dynamic_snippet_products'"/>
+ <we-select string="Product Category" data-name="product_category_opt" data-attribute-name="productCategoryId" data-no-preview="true">
+ <we-button data-select-data-attribute="-1">All Products</we-button>
+ </we-select>
+ </t>
+ </xpath>
+ </template>
+ <template id="assets_snippet_s_dynamic_snippet_products_js_000" inherit_id="website.assets_snippet_s_dynamic_snippet_carousel_js_000">
+ <xpath expr="//script[last()]" position="after">
+ <script type="text/javascript" src="/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js"/>
+ </xpath>
+ </template>
+</odoo>
diff --git a/addons/website_sale/views/snippets/s_products_searchbar.xml b/addons/website_sale/views/snippets/s_products_searchbar.xml
new file mode 100644
index 00000000..67ae6208
--- /dev/null
+++ b/addons/website_sale/views/snippets/s_products_searchbar.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+<template id="s_products_searchbar_input" name="Products Search">
+ <t t-call="website_sale.website_sale_products_search_box">
+ <t t-set="_classes" t-value="'s_wsale_products_searchbar_input'"/>
+ <t t-set="_snippet" t-value="'s_products_searchbar_input'"/>
+ </t>
+</template>
+<template id="s_products_searchbar" name="Products Search">
+ <section class="s_wsale_products_searchbar bg-200 pt48 pb48" data-vxml="001">
+ <div class="container">
+ <div class="row">
+ <div class="col-lg-8 offset-lg-2">
+ <h2>Search for a product</h2>
+ <p>We have amazing products in our shop, check them now !</p>
+ <t t-snippet-call="website_sale.s_products_searchbar_input"/>
+ </div>
+ </div>
+ </div>
+ </section>
+</template>
+
+<template id="searchbar_input_snippet_options" inherit_id="website_sale.snippet_options" name="search bar snippet options">
+ <xpath expr="." position="inside">
+ <div data-selector=".s_wsale_products_searchbar_input">
+ <we-select string="Order by" id="order_by" data-attribute-name="value" data-apply-to=".o_wsale_search_order_by">
+ <we-button data-select-attribute="">default</we-button>
+ <we-button data-select-attribute="name asc">name (A-Z)</we-button>
+ <we-button data-select-attribute="name desc">name (Z-A)</we-button>
+ <we-button data-select-attribute="list_price asc">price (low to high)</we-button>
+ <we-button data-select-attribute="list_price desc">price (high to low)</we-button>
+ </we-select>
+ <t t-set="unit">products</t>
+ <we-input string="Suggestions" data-name="product_limit_opt" data-attribute-name="limit"
+ data-apply-to=".search-query" data-select-data-attribute="" t-att-data-unit="unit"/>
+ <we-checkbox string="Description" data-dependencies="product_limit_opt" data-select-data-attribute="true" data-attribute-name="displayDescription"
+ data-apply-to=".search-query"/>
+ <we-checkbox string="Price" data-dependencies="product_limit_opt" data-select-data-attribute="true" data-attribute-name="displayPrice"
+ data-apply-to=".search-query"/>
+ <we-checkbox string="Image" data-dependencies="product_limit_opt" data-select-data-attribute="true" data-attribute-name="displayImage"
+ data-apply-to=".search-query"/>
+ </div>
+ </xpath>
+ <xpath expr="//*[@t-set='so_content_addition_selector']" position="inside">, .s_wsale_products_searchbar_input</xpath>
+</template>
+
+<template id="assets_snippet_s_products_searchbar_js_000" inherit_id="website_sale.assets_frontend">
+ <xpath expr="//script[last()]" position="after">
+ <script type="text/javascript" src="/website_sale/static/src/snippets/s_products_searchbar/000.js"/>
+ </xpath>
+</template>
+
+</odoo>
diff --git a/addons/website_sale/views/snippets/snippets.xml b/addons/website_sale/views/snippets/snippets.xml
new file mode 100644
index 00000000..6b0fb28a
--- /dev/null
+++ b/addons/website_sale/views/snippets/snippets.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+<template id="s_products_recently_viewed" name="Viewed Products">
+ <section class="s_wsale_products_recently_viewed d-none pt24 pb24" style="min-height: 400px;">
+ <div class="container">
+ <div class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_non_editable_mode_hidden o_not_editable">
+ This is a preview of the recently viewed products by the user.<br/>
+ Once the user has seen at least one product this snippet will be visible.
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close"> × </button>
+ </div>
+
+ <h3 class="text-center mb32">Recently viewed Products</h3>
+ <div class="slider o_not_editable"/>
+ </div>
+ </section>
+</template>
+
+<template id="snippets" inherit_id="website.snippets" name="e-commerce snippets">
+ <xpath expr="//t[@id='sale_products_hook']" position="replace">
+ <t t-snippet="website_sale.s_dynamic_snippet_products" t-thumbnail="/website_sale/static/src/img/snippets_thumbs/s_products_recently_viewed.svg"/>
+ </xpath>
+ <xpath expr="//t[@id='sale_recently_viewed_product_hook']" position="replace">
+ <t t-snippet="website_sale.s_products_recently_viewed" t-thumbnail="/website_sale/static/src/img/snippets_thumbs/s_products_recently_viewed.svg"/>
+ </xpath>
+ <xpath expr="//t[@id='sale_product_search_section_hook']" position="replace">
+ <t t-snippet="website_sale.s_products_searchbar" t-thumbnail="/website_sale/static/src/img/snippets_thumbs/s_products_searchbar.svg"/>
+ </xpath>
+ <xpath expr="//t[@id='sale_product_search_input_hook']" position="replace">
+ <t t-snippet="website_sale.s_products_searchbar_input" t-thumbnail="/website_sale/static/src/img/snippets_thumbs/s_products_searchbar_inline.svg"/>
+ </xpath>
+</template>
+
+<template id="snippet_options" inherit_id="website.snippet_options" name="e-commerce snippet options">
+ <xpath expr="." position="inside">
+ <div data-js="WebsiteSaleGridLayout"
+ data-selector="#products_grid .o_wsale_products_grid_table_wrapper > table"
+ data-no-check="true">
+ <we-input string="Number of products" data-set-ppg="" data-no-preview="true"/>
+ <we-select string="Number of Columns" class="o_wsale_ppr_submenu" data-no-preview="true">
+ <we-button data-set-ppr="2">2</we-button>
+ <we-button data-set-ppr="3">3</we-button>
+ <we-button data-set-ppr="4">4</we-button>
+ </we-select>
+ </div>
+
+ <div data-js="WebsiteSaleProductsItem"
+ data-selector="#products_grid .oe_product"
+ data-no-check="true">
+ <div class="o_wsale_soptions_menu_sizes">
+ <we-title>Size</we-title>
+ <table>
+ <tr>
+ <td/><td/><td/><td/>
+ </tr>
+ <tr>
+ <td/><td/><td/><td/>
+ </tr>
+ <tr>
+ <td/><td/><td/><td/>
+ </tr>
+ <tr>
+ <td/><td/><td/><td/>
+ </tr>
+ </table>
+ </div>
+
+ <we-row data-name="ribbon_options">
+ <we-select string="Ribbon" class="o_wsale_ribbon_select">
+ <we-button data-set-ribbon="" data-name="no_ribbon_opt">None</we-button>
+ <!-- Ribbons are filled in JS -->
+ </we-select>
+ <we-button data-edit-ribbon="" class="fa fa-edit" data-no-preview="true" data-dependencies="!no_ribbon_opt"/>
+ <we-button data-create-ribbon="" class="fa fa-plus text-success" data-no-preview="true"/>
+ </we-row>
+ <div class="d-none" data-name="ribbon_customize_opt">
+ <we-row string="Ribbon">
+ <we-input data-set-ribbon-html="" class="o_we_large_input" data-apply-to=".o_wsale_ribbon_dummy"/>
+ <we-button class="fa fa-check" data-save-ribbon="" title="Validate" data-no-preview="true"/>
+ <we-button class="fa fa-trash" data-delete-ribbon="" title="Delete" data-no-preview="true"/>
+ </we-row>
+ <we-colorpicker string="⌙ Background" title="" data-select-style="" data-apply-to=".o_wsale_ribbon_dummy" data-css-property="background-color" data-color-prefix="bg-"/>
+ <we-colorpicker string="⌙ Text" title="" data-select-style="" data-apply-to=".o_wsale_ribbon_dummy" data-css-property="color"/>
+ <we-select string="⌙ Mode">
+ <we-button data-set-ribbon-mode="ribbon">Slanted</we-button>
+ <we-button data-set-ribbon-mode="tag">Tag</we-button>
+ </we-select>
+ <we-select string="⌙ Position">
+ <we-button data-set-ribbon-position="left">Left</we-button>
+ <we-button data-set-ribbon-position="right">Right (only on grid view)</we-button>
+ </we-select>
+ </div>
+
+ <div name="reordering" data-no-preview="true">
+ <we-button data-change-sequence="top">Push to top</we-button>
+ <we-button data-change-sequence="up">Push up</we-button>
+ <we-button data-change-sequence="down">Push down</we-button>
+ <we-button data-change-sequence="bottom">Push to bottom</we-button>
+ </div>
+ </div>
+ <div data-selector="#wrapwrap > header"
+ data-no-check="true"
+ groups="website.group_website_designer">
+ <we-checkbox string="Show Empty Cart"
+ data-customize-website-views="website_sale.header_hide_empty_cart_link|"
+ data-no-preview="true"
+ data-reload="/"/>
+ </div>
+ </xpath>
+</template>
+
+</odoo>
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
new file mode 100644
index 00000000..0fb67f8e
--- /dev/null
+++ b/addons/website_sale/views/templates.xml
@@ -0,0 +1,1942 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <template id="_assets_primary_variables" inherit_id="website._assets_primary_variables">
+ <xpath expr="//link[last()]" position="after">
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/primary_variables.scss"/>
+ </xpath>
+ </template>
+
+ <template id="assets_backend" inherit_id="web.assets_backend">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_video_field_preview.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_backend.js"></script>
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/website_sale_dashboard.scss"/>
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/website_sale_backend.scss"/>
+ <script type="text/javascript" src="/website_sale/static/src/js/tours/website_sale_shop_backend.js"></script>
+ </xpath>
+ </template>
+
+ <template id="assets_frontend" inherit_id="website.assets_frontend">
+ <xpath expr="link[last()]" position="after">
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/website_sale.scss" />
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/website_mail.scss" />
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/website_sale_frontend.scss"/>
+ <link rel="stylesheet" type="text/scss" href="/sale/static/src/scss/sale_portal.scss"/>
+ <link rel="stylesheet" type="text/scss" href="/sale/static/src/scss/product_configurator.scss"/>
+ </xpath>
+ <xpath expr="script[last()]" position="after">
+ <script type="text/javascript" src="/sale/static/src/js/variant_mixin.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/variant_mixin.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_utils.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_payment.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_validate.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_recently_viewed.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_tracking.js"></script>
+ </xpath>
+ </template>
+
+ <template id="assets_wysiwyg" inherit_id="website.assets_wysiwyg" name="Shop Wysiwyg Editor">
+ <xpath expr="//link[last()]" position="after">
+ <link rel="stylesheet" type="text/scss" href="/website_sale/static/src/scss/website_sale.editor.scss"/>
+ <script type="text/javascript" src="/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js"/>
+ </xpath>
+ </template>
+
+ <template id="assets_editor" inherit_id="website.assets_editor" name="Shop Editor">
+ <xpath expr="//script[last()]" position="after">
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale.editor.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/website_sale_form_editor.js"></script>
+ <script type="text/javascript" src="/website_sale/static/src/js/tours/website_sale_shop_frontend.js"></script>
+ </xpath>
+ </template>
+
+ <template id="assets_common" name="tour" inherit_id="web.assets_common">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/website_sale/static/src/js/tours/website_sale_shop.js"></script>
+ </xpath>
+ </template>
+
+ <template id="assets_tests" name="Website Sale Assets Tests" inherit_id="web.assets_tests">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_buy.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_complete_flow.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_cart_recovery.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_mail.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_customize.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_custom_attribute_value.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_zoom.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_dynamic_variants.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_deleted_archived_variants.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_list_view_b2c.js"></script>
+ <script type="text/javascript" src="/website_sale/static/tests/tours/website_sale_shop_no_variant_attribute.js"></script>
+ </xpath>
+ </template>
+
+ <template id="header_cart_link" name="Header Cart Link">
+ <t t-set="website_sale_order" t-value="website.sale_get_order()" />
+ <t t-set="show_cart" t-value="true"/>
+ <li t-attf-class="#{_item_class} divider d-none"/> <!-- Make sure the cart and related menus are not folded (see autohideMenu) -->
+ <li t-attf-class="o_wsale_my_cart #{not show_cart and 'd-none'} #{_item_class}">
+ <a href="/shop/cart" t-attf-class="#{_link_class}">
+ <i t-if="_icon" class="fa fa-shopping-cart"/>
+ <span t-if="_text">My Cart</span>
+ <sup class="my_cart_quantity badge badge-primary" t-esc="website_sale_order and website_sale_order.cart_quantity or '0'" t-att-data-order-id="website_sale_order and website_sale_order.id or ''"/>
+ </a>
+ </li>
+ </template>
+
+ <template id="header_hide_empty_cart_link" inherit_id="website_sale.header_cart_link" name="Header Hide Empty Cart link" active="False">
+ <xpath expr="//t[@t-set='show_cart']" position="after">
+ <t t-set="show_cart" t-value="show_cart and website_sale_order and website_sale_order.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 ml-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 ml-lg-auto'"/>
+ <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 ml-lg-3'"/>
+ <t t-set="_link_class" t-value="'nav-link'"/>
+ </t>
+ </xpath>
+ </template>
+
+ <template id="template_header_minimalist" inherit_id="website.template_header_minimalist">
+ <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 ml-3'"/>
+ </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 ml-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 ml-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'"/>
+ <t t-set="_link_class" t-value="'nav-link'"/>
+ </t>
+ </xpath>
+ </template>
+
+ <!-- Products Search Bar input-group template -->
+ <template id="website_sale_products_search_box" inherit_id="website.website_search_box" primary="True">
+ <xpath expr="//input[@name='search']" position="attributes">
+ <attribute name="data-limit">5</attribute>
+ <attribute name="data-display-description">true</attribute>
+ <attribute name="data-display-price">true</attribute>
+ <attribute name="data-display-image">true</attribute>
+ </xpath>
+ <xpath expr="//div[@role='search']" position="attributes">
+ <attribute name="t-attf-class" remove="#{_classes}" separator=" "/>
+ </xpath>
+ <xpath expr="//div[@role='search']" position="replace">
+ <form t-attf-class="o_wsale_products_searchbar_form o_wait_lazy_js #{_classes}" t-att-action="action if action else '/shop'" method="get" t-att-data-snippet="_snippet">
+ <t>$0</t>
+ <input name="order" type="hidden" class="o_wsale_search_order_by" value=""/>
+ <t t-raw="0"/>
+ </form>
+ </xpath>
+ </template>
+
+ <template id="search" name="Search Box">
+ <t t-call="website_sale.website_sale_products_search_box">
+ <t t-set="action" t-value="keep('/shop'+ ('/category/'+slug(category)) if category else None, search=0)"/>
+ <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="search_count_box" inherit_id="website.website_search_box" active="False" customize_show="True" name="Show # found">
+ <xpath expr="//button[hasclass('oe_search_button')]" position="inside">
+ <span t-if='search' class='oe_search_found'> <small>(<t t-esc="search_count"/> found)</small></span>
+ </xpath>
+ </template>
+
+ <template id="products_item" name="Products item">
+ <t t-set="product_href" t-value="keep(product.website_url, page=(pager['page']['num'] if pager['page']['num']&gt;1 else None))" />
+
+ <t t-set="combination_info" t-value="product._get_combination_info(only_template=True, add_qty=add_qty or 1, pricelist=pricelist)"/>
+
+ <form action="/shop/cart/update" method="post" class="card oe_product_cart"
+ t-att-data-publish="product.website_published and 'on' or 'off'"
+ itemscope="itemscope" itemtype="http://schema.org/Product">
+ <div class="card-body p-1 oe_product_image">
+ <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
+ <a t-att-href="product_href" class="d-block h-100" itemprop="url">
+ <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_1024' if product_image_big else 'image_256', 'itemprop': 'image'}"
+ class="d-flex h-100 justify-content-center align-items-center"/>
+ </a>
+ </div>
+ <div class="card-body p-0 text-center o_wsale_product_information">
+ <div class="p-2 o_wsale_product_information_text">
+ <h6 class="o_wsale_products_item_title">
+ <a itemprop="name" t-att-href="product_href" t-att-content="product.name" t-field="product.name" />
+ <a role="button" t-if="not product.website_published" t-att-href="product_href" class="btn btn-sm btn-danger" title="This product is unpublished.">Unpublished</a>
+ </h6>
+ <div class="product_price" itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer">
+ <del t-attf-class="text-danger mr-2 {{'' 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-if="combination_info['price']" 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" />
+ </div>
+ </div>
+ <div class="o_wsale_product_btn"/>
+ </div>
+ <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 #{bg_class}" t-attf-style="#{text_color and ('color: %s; ' % text_color)}#{bg_color and 'background-color:' + bg_color}" t-raw="td_product['ribbon']['html'] or ''"/>
+ </form>
+ </template>
+
+ <template id="products_description" inherit_id="website_sale.products_item" active="False" customize_show="True" name="Product Description">
+ <xpath expr="//*[hasclass('product_price')]" position="after">
+ <div class="oe_subdescription" 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" customize_show="True" 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">
+ <a href="#" role="button" class="btn btn-secondary 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">
+ <t t-set="website_sale_pricelists" t-value="website.get_pricelist_available(show_visible=True)" />
+ <div t-attf-class="dropdown#{'' if website_sale_pricelists and len(website_sale_pricelists)&gt;1 else ' d-none'} #{_classes}">
+ <t t-set="curr_pl" t-value="website.get_current_pricelist()" />
+ <a role="button" href="#" class="dropdown-toggle btn btn-secondary" data-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>
+
+ <!-- /shop product listing -->
+ <template id="products" name="Products">
+ <t t-call="website.layout">
+ <t t-set="additional_title">Shop</t>
+ <div id="wrap" class="js_sale">
+ <div class="oe_structure oe_empty" id="oe_structure_website_sale_products_1"/>
+ <div class="container oe_website_sale">
+ <div class="products_pager form-inline flex-md-nowrap justify-content-between justify-content-md-center">
+ <t t-call="website_sale.search">
+ <t t-set="_classes" t-valuef="w-100 w-md-auto mt-2"/>
+ </t>
+ <t t-call="website_sale.pricelist_list">
+ <t t-set="_classes" t-valuef="mt-2 ml-md-2"/>
+ </t>
+ <t t-call="website.pager">
+ <t t-set="_classes" t-valuef="mt-2 ml-md-2"/>
+ </t>
+ </div>
+ <div class="row o_wsale_products_main_row">
+ <div t-if="enable_left_column" id="products_grid_before" class="col-lg-3"/>
+ <div id="products_grid" t-attf-class="col #{'o_wsale_layout_list' if layout_mode == 'list' else ''}">
+ <t t-if="category">
+ <t t-set='editor_msg'>Drag building blocks here to customize the header for "<t t-esc='category.name'/>" category.</t>
+ <div class="mb16" id="category_header" t-att-data-editor-message="editor_msg" t-field="category.website_description"/>
+ </t>
+ <div t-if="bins" class="o_wsale_products_grid_table_wrapper">
+ <table class="table table-borderless m-0" t-att-data-ppg="ppg" t-att-data-ppr="ppr">
+ <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">
+ <t t-set="product" t-value="td_product['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">
+ <div t-attf-class="o_wsale_product_grid_wrapper o_wsale_product_grid_wrapper_#{td_product['x']}_#{td_product['y']}">
+ <t t-call="website_sale.products_item">
+ <t t-set="product_image_big" t-value="td_product['x'] + td_product['y'] &gt; 2"/>
+ </t>
+ </div>
+ </td>
+ </t>
+ <td t-else=""/>
+ </t>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <t t-else="">
+ <div class="text-center text-muted">
+ <t t-if="not search">
+ <h3 class="mt8">No product defined</h3>
+ <p t-if="category">No product defined in category "<strong t-esc="category.display_name"/>".</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>
+ </t>
+ </div>
+ </div>
+ <div class="products_pager form-inline justify-content-center py-3">
+ <t t-call="website.pager"/>
+ </div>
+ </div>
+ <div class="oe_structure oe_empty" id="oe_structure_website_sale_products_2"/>
+ </div>
+ </t>
+ </template>
+
+ <!-- (Option) Products: Enable image full for all products : Images Full -->
+ <template id="products_images_full" name="Images Full" inherit_id="website_sale.products" active="False" customize_show="True">
+ <xpath expr="//t[@t-foreach='tr_product']//td" position="attributes">
+ <attribute name="t-attf-class" add="oe_image_full" separator=" "/>
+ </xpath>
+ </template>
+
+ <template id="sort" inherit_id="website_sale.products" customize_show="True" name="Show Sort by">
+ <xpath expr="//div[hasclass('products_pager')]/t[@t-call][last()]" position="after">
+ <t t-set="list_price_desc_label">Catalog price: High to Low</t>
+ <t t-set="list_price_asc_label">Catalog price: Low to High</t>
+ <t t-set="name_asc_label">Name: A to Z</t>
+ <t t-set="name_desc_label">Name: Z to A</t>
+ <t t-set="website_sale_sortable" t-value="[
+ (list_price_desc_label, 'list_price desc'),
+ (list_price_asc_label, 'list_price asc'),
+ (name_asc_label, 'name asc'),
+ (name_desc_label, 'name desc')
+ ]"/>
+ <t t-set="website_sale_sortable_current" t-value="[sort for sort in website_sale_sortable if sort[1]==request.params.get('order', '')]"/>
+ <div class="dropdown mt-2 ml-md-2 dropdown_sorty_by">
+ <a role="button" href="#" class="dropdown-toggle btn btn-secondary" data-toggle="dropdown">
+ <span class="d-none d-lg-inline">
+ <t t-if='len(website_sale_sortable_current)'>
+ Sorting by : <t t-raw='website_sale_sortable_current[0][0]'/>
+ </t>
+ <t t-else='1'>
+ Sort by
+ </t>
+ </span>
+ <i class="fa fa-sort-amount-asc d-lg-none"/>
+ </a>
+ <div class="dropdown-menu dropdown-menu-right" role="menu">
+ <t t-foreach="website_sale_sortable" t-as="sortby">
+ <a role="menuitem" rel="noindex,nofollow" t-att-href="keep('/shop', order=sortby[1])" class="dropdown-item">
+ <span t-raw="sortby[0]"/>
+ </a>
+ </t>
+ </div>
+ </div>
+ </xpath>
+ </template>
+
+ <template id="add_grid_or_list_option" inherit_id="website_sale.products" active="True" customize_show="True" name="Grid or List button">
+ <xpath expr="//div[hasclass('products_pager')]/t[@t-call][last()]" position="after">
+ <div class="btn-group btn-group-toggle mt-2 ml-md-2 d-none d-sm-inline-flex o_wsale_apply_layout" data-toggle="buttons">
+ <label t-attf-class="btn btn-secondary #{'active' if layout_mode != 'list' else None} fa fa-th-large o_wsale_apply_grid" title="Grid">
+ <input type="radio" name="wsale_products_layout" t-att-checked="'checked' if layout_mode != 'list' else None"/>
+ </label>
+ <label t-attf-class="btn btn-secondary #{'active' if layout_mode == 'list' else None} fa fa-th-list o_wsale_apply_list" title="List">
+ <input type="radio" name="wsale_products_layout" t-att-checked="'checked' if layout_mode == 'list' else None"/>
+ </label>
+ </div>
+ </xpath>
+ </template>
+
+ <!-- Add to cart button-->
+ <template id="categories_recursive" name="Category list">
+ <li class="nav-item">
+ <a t-att-href="keep('/shop/category/' + slug(c), category=0)" t-attf-class="nav-link #{'active' if c.id == category.id else ''}">
+ <span t-field="c.name"/>
+ </a>
+ <ul t-if="c.child_id" class="nav nav-pills flex-column nav-hierarchy">
+ <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="products_categories" inherit_id="website_sale.products" active="False" customize_show="True" name="eCommerce Categories">
+ <xpath expr="//div[@id='products_grid_before']" position="before">
+ <t t-set="enable_left_column" t-value="True"/>
+ </xpath>
+ <xpath expr="//div[@id='products_grid_before']" position="inside">
+ <button type="button" class="btn btn-link d-lg-none"
+ data-target="#wsale_products_categories_collapse" data-toggle="collapse">
+ Show categories
+ </button>
+ <div class="collapse d-lg-block" id="wsale_products_categories_collapse">
+ <ul class="nav nav-pills flex-column mb-2">
+ <li class="nav-item">
+ <a t-att-href="keep('/shop', category=0)" t-attf-class="nav-link #{'' if category else 'active'} o_not_editable">All Products</a>
+ </li>
+ <t t-foreach="categories" t-as="c">
+ <t t-call="website_sale.categories_recursive" />
+ </t>
+ </ul>
+ </div>
+ </xpath>
+ </template>
+
+ <template id="option_collapse_categories_recursive" name="Collapse Category Recursive">
+ <li class="nav-item">
+ <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)"/>
+ <i t-if="children" t-attf-class="text-primary fa #{'fa-chevron-down' if c.id in category.parents_and_self.ids else 'fa-chevron-right'}"
+ t-attf-title="#{'Unfold' if c.id in category.parents_and_self.ids else 'Fold'}"
+ t-attf-aria-label="#{'Unfold' if c.id in category.parents_and_self.ids else 'Fold'}" role="img"/>
+ <a t-att-href="keep('/shop/category/' + slug(c), category=0)" t-attf-class="nav-link #{'active' if c.id == category.id else ''}" t-field="c.name"></a>
+ <ul t-if="children" class="nav nav-pills flex-column nav-hierarchy" t-att-style="'display:block;' if c.id in category.parents_and_self.ids else 'display:none;'">
+ <t t-foreach="children" t-as="c">
+ <t t-call="website_sale.option_collapse_categories_recursive"/>
+ </t>
+ </ul>
+ </li>
+ </template>
+
+ <template id="option_collapse_products_categories" name="Collapsible Category List" inherit_id="website_sale.products_categories" active="False" customize_show="True">
+ <xpath expr="//div[@id='wsale_products_categories_collapse']/ul" position="attributes">
+ <attribute name="id">o_shop_collapse_category</attribute>
+ </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="False" customize_show="True" name="Product Attribute's Filters">
+ <xpath expr="//div[@id='products_grid_before']" position="before">
+ <t t-set="enable_left_column" t-value="True"/>
+ </xpath>
+ <xpath expr="//div[@id='products_grid_before']" position="inside">
+ <button type="button" class="btn btn-link d-lg-none"
+ data-target="#wsale_products_attributes_collapse" data-toggle="collapse">
+ Show options
+ </button>
+ <div class="collapse d-lg-block" id="wsale_products_attributes_collapse">
+ <form class="js_attributes 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" />
+ <ul class="nav nav-pills flex-column">
+ <t t-foreach="attributes" t-as="a">
+ <li t-if="a.value_ids and len(a.value_ids) &gt; 1" class="nav-item">
+ <div>
+ <strong t-field="a.name" />
+ </div>
+ <t t-if="a.display_type == 'select'">
+ <select class="form-control" name="attrib">
+ <option value="" />
+ <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'">
+ <ul class="nav nav-pills flex-column">
+ <t t-foreach="a.value_ids" t-as="v">
+ <li class="nav-item">
+ <label style="padding: 0; margin: 0" t-attf-class="nav-link#{' 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" />
+ <span style="font-weight: normal" t-field="v.name" />
+ </label>
+ </li>
+ </t>
+ </ul>
+ </t>
+ <t t-if="a.display_type == 'color'">
+ <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 #{'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>
+ </t>
+ </li>
+ </t>
+ </ul>
+ </form>
+ </div>
+ </xpath>
+ </template>
+
+ <template id="products_list_view" inherit_id="website_sale.products" active="False" customize_show="True" 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="product_edit_options" inherit_id="website.user_navbar" name="Edit Product Options">
+ <xpath expr="//li[@id='edit-page-menu']" position="after">
+ <t t-if="main_object._name == 'product.template'" t-set="action" t-value="'website_sale.product_template_action_website'" />
+ </xpath>
+ </template>
+
+ <template id="product" name="Product" track="1">
+
+ <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">
+ <section t-attf-class="container py-2 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'">
+ <div class="row">
+ <div class="col-md-4">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item">
+ <a t-att-href="keep(category=0)">Products</a>
+ </li>
+ <li 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 class="col-md-8">
+ <div class="form-inline justify-content-end">
+ <t t-call="website_sale.search">
+ <t t-set="search" t-value="False"/>
+ </t>
+ <t t-call="website_sale.pricelist_list">
+ <t t-set="_classes" t-valuef="ml-2"/>
+ </t>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6 col-xl-8">
+ <t t-call="website_sale.shop_product_carousel"/>
+ </div>
+ <div class="col-md-6 col-xl-4" id="product_details">
+ <h1 itemprop="name" t-field="product.name">Product Name</h1>
+ <span itemprop="url" style="display:none;" t-esc="product.website_url"/>
+ <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()" />
+ <div class="js_product js_main_product">
+ <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" />
+ <t t-if="combination" t-call="sale.variants">
+ <t t-set="ul_class" t-value="'flex-column'" />
+ <t t-set="parent_combination" t-value="None" />
+ </t>
+ <t t-else="">
+ <ul class="d-none js_add_cart_variants" t-att-data-attribute_exclusions="{'exclusions: []'}"/>
+ </t>
+ </t>
+ <t t-call="website_sale.product_price" />
+ <p t-if="True" class="css_not_available_msg alert alert-warning">This combination does not exist.</p>
+ <a role="button" id="add_to_cart" class="btn btn-primary btn-lg mt16 js_check_product a-submit d-block d-sm-inline-block" href="#"><i class="fa fa-shopping-cart"/> Add to Cart</a>
+ <div id="product_option_block"/>
+ </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>
+ <hr t-if="product.description_sale" />
+ <div>
+ <p t-field="product.description_sale" class="text-muted mt-3" placeholder="A short description that will also appear on documents." />
+ <div id="product_attributes_simple">
+ <hr t-if="sum([(1 if len(l.value_ids)==1 else 0) for l in product.attribute_line_ids])"/>
+ <p class="text-muted">
+ <t t-set="single_value_attributes" t-value="product.valid_product_template_attribute_line_ids._prepare_single_value_for_display()"/>
+ <t t-foreach="single_value_attributes" t-as="attribute">
+ <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>
+ <br/>
+ </t>
+ </p>
+ </div>
+ </div>
+ <hr />
+ </div>
+ </div>
+ </section>
+ <div itemprop="description" t-field="product.website_description" class="oe_structure oe_empty mt16" id="product_full_description"/>
+ </div>
+ </t>
+ </template>
+
+ <template id="product_custom_text" inherit_id="website_sale.product" active="True" customize_show="True" name="Terms and Conditions">
+ <xpath expr="//div[@id='product_details']" position="inside">
+ <p class="text-muted">
+ <a href="/shop/terms">Terms and Conditions</a><br/>
+ 30-day money-back guarantee<br/>
+ Shipping: 2-3 Business Days
+ </p>
+ </xpath>
+ </template>
+
+ <template inherit_id='website_sale.product' id="product_picture_magnify" customize_show="True" name="Image Zoom">
+ <xpath expr='//div[hasclass("js_sale")]' position='attributes'>
+ <attribute name="class" separator=" " add="ecom-zoomable zoomodoo-next" />
+ </xpath>
+ </template>
+
+ <template inherit_id='website_sale.product' id="product_picture_magnify_auto" active="False" customize_show="True" name="Automatic Image Zoom">
+ <xpath expr='//div[hasclass("js_sale")]' position='attributes'>
+ <attribute name="data-ecom-zoom-auto">1</attribute>
+ <attribute name="class" separator=" " add="ecom-zoomable zoomodoo-next" />
+
+ </xpath>
+ </template>
+
+ <template id="recommended_products" inherit_id="website_sale.product" customize_show="True" name="Alternative Products">
+ <xpath expr="//div[@id='product_full_description']" position="after">
+ <div class="container mt32" t-if="product.alternative_product_ids">
+ <h3>Alternative Products:</h3>
+ <div class="row mt16" style="">
+ <t t-foreach="product.alternative_product_ids" t-as="alt_product">
+ <div class="col-lg-2" style="width: 170px; height:130px; float:left; display:inline; margin-right: 10px; overflow:hidden;">
+ <div class="mt16 text-center" style="height: 100%;">
+ <t t-set="combination_info" t-value="alt_product._get_combination_info()"/>
+ <t t-set="product_variant" t-value="alt_product.env['product.product'].browse(combination_info['product_id'])"/>
+ <div t-if="product_variant" t-field="product_variant.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded shadow o_alternative_product o_image_64_max' }" />
+ <div t-else="" t-field="alt_product.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded shadow o_alternative_product o_image_64_max' }" />
+ <h6>
+ <a t-att-href="alt_product.website_url" style="display: block">
+ <span t-att-title="alt_product.name" t-field="alt_product.name" class="o_text_overflow" style="display: block;" />
+ </a>
+ </h6>
+ </div>
+ </div>
+ </t>
+ </div>
+ </div>
+ </xpath>
+ </template>
+
+ <template id="recently_viewed_products_product" inherit_id="website_sale.product" active="False" customize_show="True" name="Recently Viewed Products" priority="16">
+ <xpath expr="//div[@t-field='product.website_description']" position="after">
+ <t t-snippet-call="website_sale.s_products_recently_viewed"/>
+ </xpath>
+ </template>
+
+ <!-- Product options: OpenChatter -->
+ <template id="product_comment" inherit_id="website_sale.product" active="False" customize_show="True" name="Discussion and Rating" priority="15">
+ <xpath expr="//div[@t-field='product.website_description']" position="after">
+ <div class="o_shop_discussion_rating">
+ <section class="container mt16 mb16">
+ <hr/>
+ <div class="row">
+ <div class="col-lg-8 offset-lg-2">
+ <t t-call="portal.message_thread">
+ <t t-set="object" t-value="product"/>
+ <t t-set="display_rating" t-value="True"/>
+ </t>
+ </div>
+ </div>
+ </section>
+ </div>
+ </xpath>
+ </template>
+
+ <template id="product_quantity" inherit_id="website_sale.product" customize_show="True" name="Select Quantity">
+ <xpath expr="//a[@id='add_to_cart']" position="before">
+ <div class="css_quantity input-group" contenteditable="false">
+ <div class="input-group-prepend">
+ <a t-attf-href="#" class="btn btn-secondary js_add_cart_json" aria-label="Remove one" title="Remove one">
+ <i class="fa fa-minus"></i>
+ </a>
+ </div>
+ <input type="text" class="form-control quantity" data-min="1" name="add_qty" t-att-value="add_qty or 1"/>
+ <div class="input-group-append">
+ <a t-attf-href="#" class="btn btn-secondary float_left js_add_cart_json" aria-label="Add one" title="Add one">
+ <i class="fa fa-plus"></i>
+ </a>
+ </div>
+ </div>
+ </xpath>
+ </template>
+
+ <template id="product_buy_now" inherit_id="website_sale.product" active="False" customize_show="True" name="Buy Now Button">
+ <xpath expr="//a[@id='add_to_cart']" position="after">
+ <a role="button" id="buy_now" class="btn btn-outline-primary btn-lg mt16 d-block d-sm-inline-block" href="#"><i class="fa fa-bolt"/> Buy Now</a>
+ </xpath>
+ </template>
+
+ <template id="product_price">
+ <div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt16">
+ <h4 class="oe_price_h4 css_editable_mode_hidden">
+ <span t-attf-class="text-danger oe_default_price {{'' if combination_info['has_discounted_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}"
+ />
+ <b 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"/>
+ </h4>
+ <h4 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,
+ }'/>
+ </h4>
+ </div>
+ </template>
+
+ <template id="product_variants" inherit_id="website_sale.product" active="False" customize_show="True" 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" 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) &gt; 1">
+ <div t-foreach="filtered_sorted_variants" t-as="variant_id" class="custom-control custom-radio">
+ <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="custom-control-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="custom-control-label">
+ <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 class="badge badge-pill badge-secondary" t-if="diff_price != 0">
+ <t 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}"/>
+ </span>
+ </label>
+ </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&gt;=10 and '/shop/cart' or '#'">
+ <div id="wizard-step10" t-att-class="'progress-wizard-step %s' % (step == 10 and 'active' or step&gt;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&gt;=20 and '/shop/checkout' or '#'">
+ <div id="wizard-step20" t-att-class="'progress-wizard-step %s' % (step == 20 and 'active' or step&gt;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&gt;=40 and '/shop/payment' or '#'">
+ <div id="wizard-step40" t-att-class="'progress-wizard-step %s' % (step == 40 and 'active' or step&gt;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="form-group 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="form-group 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="form-group 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">A document to provide</span>
+ </label>
+ <div class="col-sm">
+ <input id="sale3" type="file" class="form-control-file s_website_form_input" name="a_document" />
+ </div>
+ </div>
+ </div>
+ <div class="form-group 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-left"><span class="fa fa-chevron-left"/> Previous</a>
+ <a role="button" class="btn btn-primary float-right 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" customize_show="True">
+ <!-- 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&gt;=30 and '/shop/extra_info' or '#'">
+ <div id="wizard-step30" t-att-class="'progress-wizard-step %s' % (step == 30 and 'active' or step&gt;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-raw="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">
+ <div t-if="line.get_description_following_lines()" t-attf-class="text-muted {{div_class}} small">
+ <t t-foreach="line.get_description_following_lines()" t-as="name_line">
+ <span><t t-esc="name_line"/></span>
+ <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>
+ <table class="mb16 table table-striped table-sm js_cart_lines" id="cart_products" t-if="website_sale_order and website_sale_order.website_order_line">
+ <thead>
+ <tr>
+ <th class="td-img">Product</th>
+ <th></th>
+ <th class="text-center td-qty">Quantity</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 colspan="2" t-if="not line.product_id.product_tmpl_id" class='td-img'></td>
+ <td align="center" t-if="line.product_id.product_tmpl_id" class='td-img'>
+ <span t-field="line.product_id.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded o_image_64_max'}" />
+ </td>
+ <td t-if="line.product_id.product_tmpl_id" 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>
+ <td class="text-center td-qty">
+ <div class="css_quantity input-group mx-auto">
+ <div class="input-group-prepend">
+ <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>
+ </div>
+ <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" />
+ <div class="input-group-append">
+ <a 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>
+ </div>
+ </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)"/>
+
+ <t t-set="list_price_converted" t-value="website.currency_id._convert(combination_info['list_price'], website_sale_order.currency_id, website_sale_order.company_id, date)"/>
+ <t groups="account.group_show_line_subtotals_tax_excluded" t-if="(website_sale_order.pricelist_id.discount_policy == 'without_discount' and website_sale_order.currency_id.compare_amounts(list_price_converted, line.price_reduce_taxexcl) == 1) or website_sale_order.currency_id.compare_amounts(line.price_unit, line.price_reduce) == 1" name="order_line_discount">
+ <del t-attf-class="#{'text-danger mr8'}" style="white-space: nowrap;" t-esc="list_price_converted" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" />
+ </t>
+ <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" />
+ <t groups="account.group_show_line_subtotals_tax_included" t-if="(website_sale_order.pricelist_id.discount_policy == 'without_discount' and website_sale_order.currency_id.compare_amounts(list_price_converted, line.price_reduce_taxinc) == 1) or website_sale_order.currency_id.compare_amounts(line.price_unit, line.price_reduce) == 1" name="order_line_discount">
+ <del t-attf-class="#{'text-danger mr8'}" style="white-space: nowrap;" t-esc="list_price_converted" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" />
+ </t>
+ <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>
+ <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}&amp;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}&amp;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" />
+ <a role="button" href="/shop" class="btn btn-secondary mb32 d-none d-xl-inline-block">
+ <span class="fa fa-chevron-left" />
+ <span class="">Continue Shopping</span>
+ </a>
+ <a role="button" t-if="website_sale_order and website_sale_order.website_order_line" class="btn btn-primary float-right d-none d-xl-inline-block" href="/shop/checkout?express=1">
+ <span class="">Process Checkout</span>
+ <span class="fa fa-chevron-right" />
+ </a>
+ <div class="oe_structure" id="oe_structure_website_sale_cart_1"/>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 col-xl-4" id="o_cart_summary">
+ <t t-call="website_sale.short_cart_summary"/>
+ <div class="d-xl-none mt8">
+ <a role="button" href="/shop" class="btn btn-secondary mb32">
+ <span class="fa fa-chevron-left" />
+ Continue<span class="d-none d-md-inline"> Shopping</span>
+ </a>
+ <a role="button" t-if="website_sale_order and website_sale_order.website_order_line" class="btn btn-primary float-right" href="/shop/checkout?express=1">
+ <span class="">Process Checkout</span>
+ <span class="fa fa-chevron-right" />
+ </a>
+ </div>
+ </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-field="line.product_id.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, '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_coupon" t-value="True"/>
+ </t>
+ </span>
+ <a role="button" class="btn btn-primary" href="/shop/cart">
+ View Cart (<t t-esc="website_sale_order.cart_quantity" /> item(s))
+ </a>
+ </div>
+ </t>
+ </template>
+
+ <template id="suggested_products_list" inherit_id="website_sale.cart_lines" customize_show="True" name="Alternative 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 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 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()" />
+ <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"/>
+ <div class="input-group-append">
+ <a href="#" role="button" class="btn btn-secondary a-submit">Apply</a>
+ </div>
+ </div>
+ </form>
+ <t t-if="request.params.get('code_not_available')" name="code_not_available">
+ <div class="alert alert-danger text-left" 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>
+ </div>
+ </div>
+ <t t-if="not only_services" groups="sale.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()" />
+ <a role="button" href="#" class='a-submit btn btn-secondary mb16 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">
+ <t t-call="website_sale.address_kanban">
+ <t t-set="actual_partner" t-value="order.partner_id" />
+ <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>
+ </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()" />
+ <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="not actual_partner or (ship.id in actual_partner.child_ids.ids)" href="#" class="btn btn-link float-right 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_coupon">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">Your Address
+ <small> or </small>
+ <a role="button" href='/web/login?redirect=/shop/checkout' class='btn btn-primary' style="margin-top: -11px">Log 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">
+ <h4 class="alert-heading">Be aware!</h4>
+ <p groups="sale.group_delivery_invoice_address">
+ 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 action="/shop/address" method="post" class="checkout_autoformat">
+ <div class="form-row">
+ <div t-attf-class="form-group #{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"/>
+ <t t-if="mode[1] == 'billing'">
+ <div t-attf-class="form-group #{error.get('email') and 'o_has_error' or ''} col-lg-6" id="div_email">
+ <label class="col-form-label" 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>
+ </t>
+ <div t-attf-class="form-group #{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>
+ <div class="w-100"/>
+ <div t-attf-class="form-group #{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="form-group #{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') &lt; zip_city.index('city')">
+ <div t-attf-class="form-group #{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="form-group #{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') &gt; zip_city.index('city')">
+ <div t-attf-class="form-group #{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="form-group #{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-control #{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="form-group #{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-control #{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">&amp;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()" />
+ <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" customize_show="True">
+ <xpath expr="//div[@id='div_phone']" position="after">
+ <div class="w-100"/>
+ <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="form-group #{error.get('company_name') and 'o_has_error' or ''} col-lg-6">
+ <label class="col-form-label font-weight-normal label-optional" for="company_name">Company Name</label>
+ <t t-set="company_name_not_editable_message">Changing company name is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.</t>
+ <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' in checkout and checkout['vat'] and not can_edit_vat else None" t-att-title="company_name_not_editable_message if 'vat' in checkout and checkout['vat'] and not can_edit_vat else None" />
+ </div>
+ <div t-attf-class="form-group #{error.get('vat') and 'o_has_error' or ''} col-lg-6 div_vat">
+ <label class="col-form-label font-weight-normal label-optional" for="vat">TIN / VAT </label>
+ <t t-set="vat_not_editable_message">Changing VAT number is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.</t>
+ <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' in checkout and checkout['vat'] and not can_edit_vat else None" t-att-title="vat_not_editable_message if 'vat' in checkout and checkout['vat'] and not can_edit_vat else None" />
+ </div>
+ </t>
+ </xpath>
+ </template>
+
+ <template id="payment" name="Payment">
+ <t t-call="website.layout">
+ <t t-set="additional_title">Shop - Select Payment Acquirer</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 class="card">
+ <div class="card-body" id="shipping_and_billing">
+ <a class='float-right 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"> &amp; 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="sale.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 clearfix mt-3" id="oe_structure_website_sale_payment_1"/>
+
+ <div id="payment_method" class="mt-3" t-if="(acquirers or tokens) and website_sale_order.amount_total">
+ <h3 class="mb24">Pay with </h3>
+ <t t-call="payment.payment_tokens_list">
+ <t t-set="mode" t-value="'payment'"/>
+ <t t-set="submit_txt">Pay Now</t>
+ <t t-set="icon_right" t-value="1"/>
+ <t t-set="icon_class" t-value="'fa-chevron-right'"/>
+ <t t-set="submit_class" t-value="'btn btn-primary'"/>
+ <t t-set="pms" t-value="tokens"/>
+ <t t-set="form_action" t-value="'/shop/payment/token'"/>
+ <t t-set="prepare_tx_url" t-value="'/shop/payment/transaction/'"/>
+ <t t-set="partner_id" t-value="partner"/>
+
+ <t t-set="back_button_icon_class" t-value="'fa-chevron-left'"/>
+ <t t-set="back_button_txt">Return to Cart</t>
+ <t t-set="back_button_class" t-value="'btn btn-secondary'"/>
+ <t t-set="back_button_link" t-value="'/shop/cart'"/>
+ </t>
+ </div>
+
+ <div t-if="not acquirers" 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 acquirers
+ </a>
+ </div>
+ <div class="js_payment mt-3" t-if="not website_sale_order.amount_total" id="payment_method">
+ <form target="_self" action="/shop/payment/validate" method="post" class="float-right">
+ <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
+ <a role="button" class="btn btn-primary a-submit" href="#">
+ <span t-if="order.amount_total &gt; 0">Pay Now <span class="fa fa-chevron-right"></span></span>
+ <span t-if="order.amount_total == 0">Confirm Order <span class="fa fa-chevron-right"></span></span>
+ </a>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="oe_structure" id="oe_structure_website_sale_payment_2"/>
+ </div>
+ </t>
+ </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>
+ <a role="button" t-if="website_sale_order and website_sale_order.website_order_line" class="btn btn-secondary float-right d-none d-xl-inline-block" href="/shop/checkout?express=1">
+ <span>Process Checkout</span>
+ </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-right' 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 class='' colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
+ <td class='td-img text-center' t-if="line.product_id.product_tmpl_id">
+ <span 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' t-if="line.product_id.product_tmpl_id">
+ <div>
+ <strong t-field="line.name_short" />
+ </div>
+ </td>
+ <td class='td-qty'>
+ <div t-esc="line.product_uom_qty" />
+ </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-call="website_sale.total">
+ <t t-set='redirect' t-value="redirect or '/shop/payment'"></t>
+ </t>
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <template id="payment_sale_note" inherit_id="payment" name="Accept Terms &amp; Conditions" customize_show="True" active="False">
+ <xpath expr="//div[@id='payment_method'][hasclass('js_payment')]" position="after">
+ <div class="custom-control custom-checkbox float-right mt-2 oe_accept_cgv_button">
+ <input type="checkbox" id="checkbox_cgv" class="custom-control-input"/>
+ <label for="checkbox_cgv" class="custom-control-label">
+ I agree to the <a target="_BLANK" href="/shop/terms">terms &amp; conditions</a>
+ </label>
+ </div>
+ </xpath>
+ </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-left mt-3">
+ <strong>Payment Information:</strong>
+ </h3>
+ <table class="table">
+ <tbody>
+ <tr>
+ <td colspan="2">
+ <t t-esc="order.get_portal_last_transaction().acquirer_id.name" />
+ </td>
+ <td class="text-right" width="100">
+ <strong>Total:</strong>
+ </td>
+ <td class="text-right" width="100">
+ <strong t-field="order.amount_total" 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_id or only_services)" />
+ <div><b>Billing <t t-if="same_shipping and not only_services"> &amp; 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="sale.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_coupon" t-value="1"/>
+ </t>
+ </div>
+ </div>
+ </div>
+ <div class="oe_structure" id="oe_structure_website_sale_confirmation_3"/>
+ </div>
+ </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">
+ <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-right border-0">Subtotal:</td>
+ <td class="text-xl-right 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-right border-0">Taxes:</td>
+ <td class="text-xl-right 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-right"><strong>Total:</strong></td>
+ <td class="text-xl-right">
+ <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" customize_show="True" name="Promo Code">
+ <xpath expr="//div[@id='cart_total']//table/tr[last()]" position="after">
+ <tr t-if="not hide_coupon">
+ <td colspan="3" class="text-center text-xl-right 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 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 'alert-success') 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-right" target="_blank" aria-label="Edit" title="Edit"
+ t-att-href="'/web#model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % ('payment.acquirer', payment_tx_id.acquirer_id.id, 'payment.action_payment_acquirer')">
+ <i class="fa fa-pencil"></i>
+ </a>
+ <t t-if="payment_tx_id.state == 'pending'">
+ <t t-raw="payment_tx_id.acquirer_id.pending_msg"/>
+ </t>
+ <t t-if="payment_tx_id.state == 'done'">
+ <span t-if='payment_tx_id.acquirer_id.done_msg' t-raw="payment_tx_id.acquirer_id.done_msg"/>
+ </t>
+ <t t-if="payment_tx_id.state == 'cancel'">
+ <t t-raw="payment_tx_id.acquirer_id.cancel_msg"/>
+ </t>
+ <t t-if="payment_tx_id.state == 'authorized'">
+ <span>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>
+ <div t-if="payment_tx_id.acquirer_id.provider == 'transfer' and order.reference" class="card-body">
+ <b>Communication: </b><span t-esc='order.reference'/>
+ </div>
+
+ <div t-if="payment_tx_id.acquirer_id.qr_code and (payment_tx_id.acquirer_id.provider == 'transfer')">
+ <t t-set="qr_code" t-value="payment_tx_id.acquirer_id.journal_id.bank_account_id.build_qr_code_url(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>
+ </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/page/e-commerce?utm_source=db&amp;utm_medium=website">Open Source eCommerce</a>
+ </t>
+ <t t-set="_utm_medium" t-valuef="website"/>
+ </t>
+ </xpath>
+ </template>
+
+ <!-- User Navbar -->
+ <template id="user_navbar_inherit_website_sale" inherit_id="website.user_navbar">
+ <xpath expr="//div[@id='o_new_content_menu_choices']//div[@name='module_website_sale']" position="attributes">
+ <attribute name="name"/>
+ <attribute name="t-att-data-module-id"/>
+ <attribute name="t-att-data-module-shortdesc"/>
+ <attribute name="groups">sales_team.group_sale_manager</attribute>
+ </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']//td[@id='product_name']/*" position="replace">
+ <a t-if="line.product_id.website_published" t-att-href="line.product_id.website_url">
+ <span t-field="line.name" />
+ </a>
+ <t t-if="not line.product_id.website_published">
+ <span t-field="line.name" />
+ </t>
+ </xpath>
+ </template>
+
+ <template id="terms" name="Terms &amp; Conditions">
+ <t t-call="website.layout">
+ <div id="wrap">
+ <div class="oe_structure" id="oe_structure_website_sale_terms_1">
+ <section class="s_title" data-vcss="001">
+ <div class="container">
+ <div class="row">
+ <div class="col-lg-12">
+ <h1 class="text-center">Terms &amp;amp; Conditions</h1>
+ <div class="card s_well clearfix">
+ <div class="card-body">
+ <ul>
+ <li>The <b>Intellectual Property</b> disclosure will inform users that the contents, logo and other visual media you created is your property and is protected by copyright laws.</li>
+ <li>A <b>Termination</b> clause will inform that users’ accounts on your website and mobile app or users’ access to your website and mobile (if users can’t have an account with you) can be terminated in case of abuses or at your sole discretion.</li>
+ <li>A <b>Governing Law</b> will inform users which laws govern the agreement. This should the country in which your company is headquartered or the country from which you operate your web site and mobile app.</li>
+ <li>A <b>Links To Other Web Sites</b> clause will inform users that you are not responsible for any third party web sites that you link to. This kind of clause will generally inform users that they are responsible for reading and agreeing (or disagreeing) with the Terms and Conditions or Privacy Policies of these third parties.</li>
+ <li>If your website or mobile apps allows users to create content and make that content public to other users, a <b>Content</b> section will inform users that they own the rights to the content they have created.<br/>The “Content” clause usually mentions that users must give you (the website or mobile app developer) a license so that you can share this content on your website/mobile app and to make it available to other users.<br/>Because the content created by users is public to other users, a DMCA notice clause (or Copyright Infringement ) section is helpful to inform users and copyright authors that, if any content is found to be a copyright infringement, you will respond to any DMCA take down notices received and you will take down the content.</li>
+ <li>A <b>Limit What Users Can Do</b> clause can inform users that by agreeing to use your service, they’re also agreeing to not do certain things. This can be part of a very long and thorough list in your Terms and Conditions agreements so as to encompass the most amount of negative uses.</li>
+ </ul>
+ <small class="text-muted float-right">Source: https://termsfeed.com/blog/sample-terms-and-conditions-template</small>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+ <section class="s_text_block" data-snippet="s_text_block">
+ <div class="container">
+ <div class="row">
+ <div class="col-lg-12 mb16 mt16">
+ <p style='white-space:pre' t-esc="website.company_id.invoice_terms"/>
+ </div>
+ </div>
+ </div>
+ </section>
+ </div>
+ <div class="oe_structure" id="oe_structure_website_sale_terms_2"/>
+ </div>
+ </t>
+ </template>
+
+ <template id="website_sale.shop_product_carousel" name="Shop Product Carousel">
+ <t t-set="product_images" t-value="product_variant._get_images() if product_variant else product._get_images()"/>
+ <div id="o-carousel-product" class="carousel slide" data-ride="carousel" data-interval="0">
+ <div class="carousel-outer position-relative">
+ <div class="carousel-inner 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 ''}">
+ <div t-if="product_image._name == 'product.image' and product_image.embed_code" class="d-flex align-items-center justify-content-center h-100 embed-responsive embed-responsive-16by9">
+ <t t-raw="product_image.embed_code"/>
+ </div>
+ <div t-else="" t-field="product_image.image_1920" class="d-flex align-items-center justify-content-center h-100" t-options='{"widget": "image", "preview_image": "image_1024", "class": "product_detail_img mh-100", "alt-field": "name", "zoom": product_image.can_image_1024_be_zoomed and "image_1920", "itemprop": "image"}'/>
+ </div>
+ </t>
+ </div>
+ <t t-if="len(product_images) > 1">
+ <a class="carousel-control-prev" href="#o-carousel-product" role="button" data-slide="prev">
+ <span class="fa fa-chevron-left p-2" role="img" aria-label="Previous" title="Previous"/>
+ </a>
+ <a class="carousel-control-next" href="#o-carousel-product" role="button" data-slide="next">
+ <span class="fa fa-chevron-right p-2" role="img" aria-label="Next" title="Next"/>
+ </a>
+ </t>
+ </div>
+ <div t-ignore="True" class="d-none d-md-block text-center">
+ <ol t-if="len(product_images) > 1" class="carousel-indicators d-inline-block position-static mx-auto my-0 p-1 text-left">
+ <t t-foreach="product_images" t-as="product_image"><li t-attf-class="d-inline-block m-1 align-top {{'active' if product_image_first else ''}}" data-target="#o-carousel-product" t-att-data-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_contain", "alt-field": "name"}'/>
+ <i t-if="product_image._name == 'product.image' and product_image.embed_code" class="fa fa-2x fa-play-circle-o o_product_video_thumb"/>
+ </li></t>
+ </ol>
+ </div>
+ </div>
+ </template>
+
+
+ <record id="view_website_sale_website_form" model="ir.ui.view">
+ <field name="name">website_sale.website.form</field>
+ <field name="model">website</field>
+ <field name="inherit_id" ref="website.view_website_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//notebook" position="inside">
+ <page string="Product Page Extra Fields" groups="base.group_no_one">
+ <field name="shop_extra_field_ids" context="{'default_website_id': active_id}">
+ <tree editable="bottom">
+ <field name="sequence" widget="handle"/>
+ <field name="field_id"/>
+ </tree>
+ </field>
+ </page>
+ </xpath>
+ </field>
+ </record>
+
+ <template id="ecom_show_extra_fields" inherit_id="website_sale.product" active="False" customize_show="True" name="Show Extra Fields">
+ <xpath expr="//div[@id='product_details']" position="inside">
+ <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>
+ </xpath>
+ </template>
+
+</odoo>
diff --git a/addons/website_sale/views/website_sale_visitor_views.xml b/addons/website_sale/views/website_sale_visitor_views.xml
new file mode 100644
index 00000000..9454a921
--- /dev/null
+++ b/addons/website_sale/views/website_sale_visitor_views.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <!--product history-->
+ <record id="website_sale_visitor_page_view_tree" model="ir.ui.view">
+ <field name="name">website.track.view.tree</field>
+ <field name="model">website.track</field>
+ <field name="arch" type="xml">
+ <tree string="Visitor Product Views History" create="0">
+ <field name="visitor_id"/>
+ <field name="product_id"/>
+ <field name="visit_datetime"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="website_sale_visitor_page_view_graph" model="ir.ui.view">
+ <field name="name">website.track.view.graph</field>
+ <field name="model">website.track</field>
+ <field name="arch" type="xml">
+ <graph string="Visitor Product Views" sample="1">
+ <field name="product_id"/>
+ </graph>
+ </field>
+ </record>
+
+ <record id="website_sale_visitor_product_action" model="ir.actions.act_window">
+ <field name="name">Product Views History</field>
+ <field name="res_model">website.track</field>
+ <field name="view_mode">tree</field>
+ <field name="view_ids" eval="[(5, 0, 0),
+ (0, 0, {'view_mode': 'tree', 'view_id': ref('website_sale_visitor_page_view_tree')}),
+ (0, 0, {'view_mode': 'graph', 'view_id': ref('website_sale_visitor_page_view_graph')}),
+ ]"/>
+ <field name="domain">[('visitor_id', '=', active_id), ('product_id', '!=', False)]</field>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_empty_folder">
+ No product views yet for this visitor
+ </p>
+ </field>
+ </record>
+
+ <record id="website_sale_visitor_page_view_search" model="ir.ui.view">
+ <field name="name">website.track.view.search</field>
+ <field name="model">website.track</field>
+ <field name="inherit_id" ref="website.website_visitor_page_view_search"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='url']" position="after">
+ <field name="product_id"/>
+ </xpath>
+ <xpath expr="//filter[@name='type_url']" position="after">
+ <filter string="Products" name="type_product" domain="[('product_id', '!=', False)]"/>
+ </xpath>
+ <xpath expr="//filter[@name='group_by_url']" position="after">
+ <filter string="Product" name="group_by_product" domain="[]" context="{'group_by': 'product_id'}"/>
+ </xpath>
+ </field>
+ </record>
+
+ <!-- website visitor views -->
+ <record id="website_sale_visitor_view_form" model="ir.ui.view">
+ <field name="name">website.visitor.view.form</field>
+ <field name="model">website.visitor</field>
+ <field name="inherit_id" ref="website.website_visitor_view_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//button[@name='%(website.website_visitor_page_action)d']" position="after">
+ <button name="%(website_sale.website_sale_visitor_product_action)d" type="action"
+ class="oe_stat_button"
+ icon="fa-tags">
+ <field name="visitor_product_count" widget="statinfo" string="Product views"/>
+ </button>
+ </xpath>
+ <xpath expr="//group[@id='visits']/field[@name='page_ids']" position="after">
+ <field name="product_ids" widget="many2many_tags"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="website_sale_visitor_view_tree" model="ir.ui.view">
+ <field name="name">website.visitor.view.tree</field>
+ <field name="model">website.visitor</field>
+ <field name="inherit_id" ref="website.website_visitor_view_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='page_ids']" position="after">
+ <field name="product_ids" widget="many2many_tags"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="website_sale_visitor_view_kanban" model="ir.ui.view">
+ <field name="name">website.visitor.view.kanban</field>
+ <field name="model">website.visitor</field>
+ <field name="inherit_id" ref="website.website_visitor_view_kanban"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='page_ids']" position="after">
+ <field name="product_ids"/>
+ </xpath>
+ <xpath expr="//div[@id='o_page_count']" position="after">
+ <div id="o_product_count">Visited Products<span class="float-right font-weight-bold"><field name="product_count"/></span></div>
+ </xpath>
+ </field>
+ </record>
+
+ <!-- website track views -->
+ <record id="website_sale_visitor_track_view_tree" model="ir.ui.view">
+ <field name="name">website.track.view.tree</field>
+ <field name="model">website.track</field>
+ <field name="inherit_id" ref="website.website_visitor_track_view_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='url']" position="after">
+ <field name="product_id"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="website_sale_visitor_track_view_graph" model="ir.ui.view">
+ <field name="name">website.track.view.graph</field>
+ <field name="model">website.track</field>
+ <field name="inherit_id" ref="website.website_visitor_track_view_graph"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='url']" position="after">
+ <field name="product_id"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>