Introduce comprehensive website sale views and templates, enhancing product visibility, adding product creation forms, and integrating dine360 order channels and online ordering functionalities.

This commit is contained in:
Alaguraj0361 2026-03-21 17:21:16 +05:30
parent 5531400dc3
commit be1c9bd35e
31 changed files with 7770 additions and 20 deletions

View File

@ -15,21 +15,47 @@ class SaleOrderOnline(models.Model):
# order_source is now canonical field from dine360_order_channels (pos.order)
# We add it to sale.order for tracking which channel the web sale originated from
order_source = fields.Selection([
('walk_in', 'Walk-In'),
('phone', 'Phone'),
('online', 'Online / eCommerce'),
('whatsapp', 'WhatsApp'),
('online', 'Online'),
('phone', 'Phone'),
('whatsapp', 'WhatsApp'),
('social_media', 'Social Media'),
('platform', 'Platform'),
('kiosk', 'Kiosk'),
('qr', 'QR Code'),
], string='Order Source', default='online')
('in_person', 'In-Person (Walk-in/Dine-in)'),
('kiosk', 'Store Self-Order (Kiosk)'),
('party_order', 'Party Order'),
('platform_integration', 'Platform Integration (3rd Party)'),
], string='Order Source', default='online', tracking=True)
fulfilment_type = fields.Selection([
('dine_in', 'Dine-In'),
('pickup', 'Pickup'),
('pickup', 'Pickup'),
('delivery', 'Delivery'),
], string='Fulfilment Type', default='pickup')
('dine_in', 'Dine-In'),
('walk_in', 'Walk-In'),
], string='Fulfillment Type', default='pickup', tracking=True)
payment_option = fields.Selection([
('in_store', 'In Store'),
('terminal_in_store', 'Payment Terminal (In Store)'),
('terminal_customer', 'Payment Terminal (Customer Place)'),
('online_gateway', 'Online Payment Gateway'),
('cash', 'Cash'),
('interac', 'Interac'),
], string='Payment Option', tracking=True)
telephone_number = fields.Char('Telephone Number')
reservation_source = fields.Selection([
('online', 'Online'),
('phone', 'Phone'),
('staff', 'Staff'),
], string='Reservation Source', tracking=True)
reservation_status = fields.Selection([
('draft', 'Request Received'),
('confirmed', 'Confirmed'),
('arrived', 'Arrived'),
('seated', 'Seated'),
('cancelled', 'Cancelled'),
], string='Reservation Status', default='draft', tracking=True)
def _create_pos_order_for_kds(self, sale_order):
"""

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="cart_service_mode" inherit_id="website_sale.cart" name="Service Mode Selector" priority="1000">
<!-- Ultra-robust XPath: Target the very top of the entire shop checkout container -->
<xpath expr="//div[contains(@class, 'oe_website_sale')]" position="before" optional="1">
<xpath expr="//div[hasclass('col')]/h3" position="before">
<div class="container container-fluid mt-4">
<div id="service_mode_selector" class="mb-4 bg-white p-3 p-md-4 rounded-4 shadow-sm border" style="border-left: 5px solid #FECD4F !important;">
<div class="d-flex align-items-center mb-3">
@ -13,7 +12,6 @@
</div>
<div class="d-flex flex-wrap gap-2 gap-md-3">
<!-- Pickup Option -->
<label class="service-option position-relative flex-fill cursor-pointer m-0">
<input type="radio" name="fulfilment_type" value="pickup" class="d-none" t-att-checked="'checked' if website_sale_order and website_sale_order.fulfilment_type == 'pickup' else None"/>
<div class="service-card p-3 rounded-3 border text-center transition-all h-100 d-flex flex-column justify-content-center">
@ -23,7 +21,6 @@
</div>
</label>
<!-- Delivery Option -->
<label class="service-option position-relative flex-fill cursor-pointer m-0">
<input type="radio" name="fulfilment_type" value="delivery" class="d-none" t-att-checked="'checked' if website_sale_order and website_sale_order.fulfilment_type == 'delivery' else None"/>
<div class="service-card p-3 rounded-3 border text-center transition-all h-100 d-flex flex-column justify-content-center">

View File

@ -55,9 +55,12 @@ class PosOrderChannels(models.Model):
# -----------------------------------------------------------
# Social Media: reference
# -----------------------------------------------------------
# ----------------------------------------------------------- # Social Media: reference
social_ref = fields.Char('Social Ref', help='Instagram/Facebook message/post reference')
# Telephone: customer phone for phone orders
telephone_number = fields.Char('Telephone Number', help='Customer phone number for telephone orders')
# -----------------------------------------------------------
# Computed helpers
# -----------------------------------------------------------
@ -112,6 +115,7 @@ class PosOrderChannels(models.Model):
fields_dict['delivery_notes'] = ui_order.get('delivery_notes', '')
fields_dict['whatsapp_number'] = ui_order.get('whatsapp_number', '')
fields_dict['social_ref'] = ui_order.get('social_ref', '')
fields_dict['telephone_number'] = ui_order.get('telephone_number', '')
return fields_dict
# -----------------------------------------------------------
@ -128,6 +132,7 @@ class PosOrderChannels(models.Model):
result['delivery_notes'] = order.delivery_notes or ''
result['whatsapp_number'] = order.whatsapp_number or ''
result['social_ref'] = order.social_ref or ''
result['telephone_number'] = order.telephone_number or ''
return result

View File

@ -21,6 +21,7 @@ patch(Order.prototype, {
this.delivery_notes = '';
this.whatsapp_number = '';
this.social_ref = '';
this.telephone_number = '';
},
export_as_JSON() {
@ -34,6 +35,7 @@ patch(Order.prototype, {
json.delivery_notes = this.delivery_notes;
json.whatsapp_number = this.whatsapp_number;
json.social_ref = this.social_ref;
json.telephone_number = this.telephone_number;
return json;
},
@ -48,6 +50,7 @@ patch(Order.prototype, {
this.delivery_notes = json.delivery_notes || '';
this.whatsapp_number = json.whatsapp_number || '';
this.social_ref = json.social_ref || '';
this.telephone_number = json.telephone_number || '';
},
setOrderSource(source) {
@ -97,6 +100,7 @@ patch(Order.prototype, {
result.delivery_notes = this.delivery_notes;
result.whatsapp_number = this.whatsapp_number;
result.social_ref = this.social_ref;
result.telephone_number = this.telephone_number;
return result;
},

View File

@ -20,6 +20,14 @@
</div>
</div>
<!-- Phone number field -->
<div t-if="orderSource === 'phone'" class="channel-extra">
<input type="text" class="form-control form-control-sm"
placeholder="📞 Phone Number"
t-att-value="currentOrder.telephone_number"
t-on-change="(ev) => { currentOrder.telephone_number = ev.target.value; }"/>
</div>
<!-- WhatsApp number field -->
<div t-if="orderSource === 'whatsapp'" class="channel-extra">
<input type="text" class="form-control form-control-sm"

View File

@ -1,5 +1,30 @@
/* Placeholder for custom navbar styling */
/* Custom POS Navbar styling */
.pos .pos-topheader {
/* Custom style placeholder */
background: inherit;
}
.btn-dashboard:hover {
background: linear-gradient(135deg, #a00d0e 0%, #d61112 100%) !important;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
}
/* Category Sidebar Red Background */
.pos .category-button {
background: #d61112 !important;
color: white !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
transition: all 0.2s ease;
}
.pos .category-button:hover {
background: #a00d0e !important;
transform: scale(1.02);
}
.pos .category-button.active {
background: #111 !important;
/* Contrast active category */
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.5) !important;
border: 1px solid #FECD4F !important;
}

View File

@ -3,9 +3,9 @@
<t t-inherit="point_of_sale.Navbar" t-inherit-mode="extension" owl="1">
<xpath expr="//img[hasclass('pos-logo')]" position="after">
<a href="/" class="btn-dashboard ms-2 d-flex align-items-center justify-content-center text-white text-decoration-none"
style="background-color: #d61112; border-radius: 4px; padding: 0 15px; height: 34px; font-weight: bold; border: 1px solid rgba(255,255,255,0.2);">
style="background: linear-gradient(135deg, #d61112 0%, #a00d0e 100%); border-radius: 6px; padding: 0 25px; height: 100%; font-weight: 700; border: 1px solid rgba(255,255,255,0.3); transition: all 0.3s ease; box-shadow: 0 2px 5px rgba(0,0,0,0.2);">
<i class="fa fa-th-large me-2"></i>
<span>Dashboard</span>
<span style="font-size: 14px;">Dashboard</span>
</a>
</xpath>
</t>

2751
tmp_templates.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<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">
<group name="sale_info_group" position="inside">
<field name="website_id"
invisible="not website_id"
groups="website.group_multi_website"/>
</group>
</field>
</record>
</odoo>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="crm_team_view_kanban_dashboard" model="ir.ui.view">
<field name="name">crm.team.view.kanban.dashboard.inherit.website.sale</field>
<field name="model">crm.team</field>
<field name="inherit_id" ref="sales_team.crm_team_view_kanban_dashboard"/>
<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" class="me-1"/>
Abandoned Carts to Recover
</a>
</div>
</div>
<div class="col-4 text-end">
<field name="abandoned_carts_amount" widget="monetary"/>
</div>
</div>
</xpath>
</data>
</field>
</record>
</odoo>

View File

@ -0,0 +1,20 @@
<?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="priority">10</field>
<field name="inherit_id" ref="digest.digest_digest_view_form" />
<field name="arch" type="xml">
<group name="kpi_sales" position="attributes">
<attribute name="string">Sales</attribute>
<attribute name="groups">sales_team.group_sale_salesman_all_leads</attribute>
</group>
<group name="kpi_sales" position="inside">
<field name="kpi_website_sale_total"/>
</group>
</field>
</record>
</odoo>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="product_attribute_view_form" model="ir.ui.view">
<field name="name">product.attribute.view.form</field>
<field name="model">product.attribute</field>
<field name="inherit_id" ref="product.product_attribute_view_form"/>
<field name="arch" type="xml">
<group name="main_fields" position="inside">
<group name="ecommerce_main_fields">
<field name="visibility" string="eCommerce Filter Visibility" widget="radio"/>
</group>
</group>
</field>
</record>
<record id="attribute_tree_view" model="ir.ui.view">
<field name="name">product.attribute.tree</field>
<field name="model">product.attribute</field>
<field name="inherit_id" ref="product.attribute_tree_view"/>
<field name="arch" type="xml">
<field name="create_variant" position="after">
<field name="visibility" string="eCommerce Filter Visibility"/>
</field>
</field>
</record>
</odoo>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_document_form" model="ir.ui.view">
<field name="name">product.document.form.website_sale</field>
<field name="model">product.document</field>
<field name="inherit_id" ref="sale.product_document_form"/>
<field name="arch" type="xml">
<sheet position="inside">
<group name="website_sale" string="E-commerce">
<field name="shown_on_product_page"/>
</group>
</sheet>
</field>
</record>
<record id="product_document_kanban" model="ir.ui.view">
<field name="name">product.document.kanban.website_sale</field>
<field name="model">product.document</field>
<field name="inherit_id" ref="sale.product_document_kanban"/>
<field name="arch" type="xml">
<div name="bottom" position="inside">
<div class="mt-2">
<span>Show on product page</span>
<field name="shown_on_product_page" class="ms-2" widget="boolean_toggle"/>
</div>
</div>
</field>
</record>
<record id="product_document_list" model="ir.ui.view">
<field name="name">product.document.list.website_sale</field>
<field name="model">product.document</field>
<field name="inherit_id" ref="sale.product_document_list"/>
<field name="arch" type="xml">
<field name="attached_on" position="after">
<field name="shown_on_product_page"/>
</field>
</field>
</record>
<record id="product_document_search" model="ir.ui.view">
<field name="name">product.document.search.sale</field>
<field name="model">product.document</field>
<field name="inherit_id" ref="sale.product_document_search"/>
<field name="arch" type="xml">
<search position="inside">
<separator/>
<filter name="e_commerce" string="Show on Ecommerce" domain="[('shown_on_product_page', '=', True)]"/>
</search>
</field>
</record>
</odoo>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="product_product_view_form_add" model="ir.ui.view">
<field name="name">product.product.view.form.add</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<form js_class="website_new_content_form">
<group name="pricing">
<field name="website_url" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="currency_id" invisible='1'/>
<field name="cost_currency_id" invisible="1"/>
<field name="name" placeholder="e.g. Cheese Burger" string="Product Name"/>
<label for="list_price" class="mt-1"/>
<div name="Sales Price">
<field name="list_price" class="oe_inline" widget='monetary'
options="{'currency_field': 'currency_id', 'field_digits': True}"/>
<field name="tax_string"/>
</div>
<field name="taxes_id" widget="many2many_tags" context="{'default_type_tax_use':'sale', 'search_default_sale': 1}" options="{'create': false, 'create_edit': false}"/>
</group>
</form>
</field>
</record>
<record id="product_product_action_add" model="ir.actions.act_window">
<field name="name">New Product</field>
<field name="res_model">product.product</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="view_id" ref="product_product_view_form_add"/>
</record>
</odoo>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Product Tags -->
<record id="product_tag_form_view_inherit_website_sale" model="ir.ui.view">
<field name="name">product.tag.form.inherit.website.sale</field>
<field name="model">product.tag</field>
<field name="inherit_id" ref="product.product_tag_form_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="visible_on_ecommerce"/>
<field name="color"
widget='color'
invisible="not visible_on_ecommerce"/>
<field name="image"
widget="image"
options="{'size': [0, 55]}"
invisible="not visible_on_ecommerce"/>
<p class="text-muted" colspan="2" invisible="not visible_on_ecommerce">
If an image is set, the color will not be used on eCommerce.
</p>
</field>
</field>
</record>
<record id="product_tag_tree_view_inherit_website_sale" model="ir.ui.view">
<field name="name">product.tag.tree.inherit.website.sale</field>
<field name="model">product.tag</field>
<field name="inherit_id" ref="product.product_tag_tree_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="visible_on_ecommerce" optional="show"/>
<field name="color"
widget="color"
optional="hide"
readonly="1"
invisible="not visible_on_ecommerce"/>
<field name="image"
widget="image"
options="{'size': [0, 30]}"
optional="hide"
invisible="not visible_on_ecommerce"/>
</field>
</field>
</record>
</odoo>

View File

@ -0,0 +1,386 @@
<?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">
<filter name="consumable" position="after">
<separator/>
<filter string="Published" name="published" domain="[('is_published', '=', True)]"/>
</filter>
</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 name="additional_product_tag_ids" position="after">
<field name="ribbon_id" options="{'no_quick_create': True}" 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">
<tree position="attributes">
<attribute name="default_order">website_sequence</attribute>
</tree>
<field name="priority" position="before">
<field name="website_sequence" widget="handle"/>
</field>
<field name="website_id" position="after">
<field name="public_categ_ids" widget="many2many_tags" string="Categories" optional="show"/>
<field name="is_published" string="Is Published" optional="hide"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="product_template_view_kanban_website_sale">
<field name="name">product.template.view.kanban.website_sale</field>
<field name="mode">primary</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_kanban_view"/>
<field name="arch" type="xml">
<kanban position="attributes">
<attribute name="default_order">website_sequence</attribute>
</kanban>
<field name="id" position="after">
<field name="website_sequence"/>
</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"/>
<field name="search_view_id" ref="product_template_search_view_website"/>
<field name="context">{'search_default_published': 1, 'tree_view_ref':'website_sale.product_template_view_tree_website_sale', 'kanban_view_ref':'website_sale.product_template_view_kanban_website_sale'}</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_only_website_form_view">
<field name="name">product.template.product.only.website.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<div name="pricing" position="after">
<label for="compare_list_price" groups="website_sale.group_product_price_comparison"/>
<div class="o_row" groups="website_sale.group_product_price_comparison">
<del class="oe_read_only">
<field name="compare_list_price" nolabel="1" widget="monetary" options="{'currency_field': 'currency_id', 'field_digits': True}"/>
</del>
<field name="compare_list_price" nolabel="1" widget="monetary" options="{'currency_field': 'currency_id', 'field_digits': True}" class="oe_edit_only"/>
</div>
</div>
<field name="uom_id" position="after">
<label for="base_unit_count" groups="website_sale.group_show_uom_price"/>
<div name="base_unit_price" groups="website_sale.group_show_uom_price" class="d-flex flex-row">
<field name="base_unit_count" invisible="product_variant_count &gt; 1" style="width: 4rem;"/>
<field name="base_unit_id" options="{'no_open': True}" invisible="product_variant_count &gt; 1" placeholder="Specify unit" style="width: 10rem;"/>
<div class="d-flex flex-row" invisible="base_unit_price == 0 or product_variant_count &gt; 1">
(<field name="base_unit_price" class="oe_inline"/> / <field name="base_unit_name" class="oe_inline"/>)
</div>
<span class='text-muted' invisible="product_variant_count &lt;= 1">Based on variants</span>
</div>
</field>
</field>
</record>
<record model="ir.ui.view" id="product_product_normal_website_form_view">
<field name="name">product.product.normal.view.website</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<field name="uom_id" position="after">
<label for="base_unit_count" groups="website_sale.group_show_uom_price"/>
<div name="base_unit_price" groups="website_sale.group_show_uom_price" class="d-flex flex-row">
<field name="base_unit_count" style="width: 4rem;"/>
<field name="base_unit_id" options="{'no_open': True}" placeholder="Specify unit" style="width: 10rem;"/>
<div class="d-flex flex-row" invisible="base_unit_price == 0">
(<field name="base_unit_price" class="oe_inline"/> / <field name="base_unit_name" class="oe_inline"/>)
</div>
</div>
</field>
</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" invisible="not sale_ok"/>
</div>
<group name="upsell" position="attributes">
<attribute name="invisible">0</attribute>
</group>
<group name="upsell" position="inside">
<field name="accessory_product_ids" widget="many2many_tags" invisible="not sale_ok"
placeholder="Suggested accessories in the eCommerce cart"/>
<field name="alternative_product_ids" widget="many2many_tags"
domain="[('id', '!=', id), '|', ('company_id', '=', company_id), ('company_id', '=', False)]"
invisible="not sale_ok"
placeholder="Displayed in bottom of product pages"/>
</group>
<xpath expr="//page[@name='sales']/group[@name='sale']" position="inside">
<group string="eCommerce Shop" name="shop" invisible="not sale_ok">
<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="website_ribbon_id" groups="base.group_no_one" options="{'no_quick_create': True}"/>
</group>
<group name="product_template_images" string="Extra Product Media" invisible="not sale_ok">
<field name="product_template_image_ids" class="o_website_sale_image_list" context="{'default_name': name}" mode="kanban" add-label="Add a Media" nolabel="1"/>
</group>
</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">
<group name="pricing" position="inside">
<label for="base_unit_count" groups="website_sale.group_show_uom_price"/>
<div name="base_unit_price" groups="website_sale.group_show_uom_price" class="d-flex flex-row">
<field name="base_unit_count" style="width: 4rem;"/>
<field name="base_unit_id" options="{'no_open': True}" placeholder="Specify unit" style="width: 10rem;"/>
<div class="d-flex flex-row" invisible="base_unit_price == 0">
(<field name="base_unit_price" class="oe_inline"/> / <field name="base_unit_name" class="oe_inline"/>)
</div>
</div>
</group>
<group name="packaging" position="after">
<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" add-label="Add a Media" nolabel="1"/>
</group>
<group name="sales" string="Sales" groups="base.group_no_one">
<field name="ribbon_id" options="{'no_quick_create': True}"/>
</group>
</group>
</field>
</record>
<!-- Product ribbon -->
<record id="product_ribbon_form_view" model="ir.ui.view">
<field name="name">product.ribbon form view</field>
<field name="model">product.ribbon</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="html" widget="char"/>
<field name="text_color"/>
</group>
<group>
<field name="html_class"/>
<field name="bg_color"/>
</group>
</group>
</sheet>
</form>
</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="float-start">
<group class="col-md-6 pe-3">
<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="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="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="product_ribbon_view_tree" model="ir.ui.view">
<field name="name">product.ribbon.tree</field>
<field name="model">product.ribbon</field>
<field name="arch" type="xml">
<tree string="Products Ribbon">
<field name="html" string="Name"/>
</tree>
</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">
<group name="pricelist_availability" position="after">
<group name="pricelist_website" string="Website">
<field name="company_id" invisible="1"/>
<field name="website_id" options="{'no_create': True}"/>
<field name="selectable"/>
<field name="code"/>
</group>
</group>
</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" 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" invisible="embed_code">
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="'text-bg-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="'text-bg-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="'text-bg-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>

View File

@ -0,0 +1,264 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form_inherit_sale" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.sale</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="sale.res_config_settings_view_form"/>
<field name="arch" type="xml">
<!-- Remove customer accounts setting from sales settings tab -->
<!-- It must not be in the view at all to make sure settings can be saved
(because auth_signup_uninvited is specified as required) -->
<setting id="auth_signup_documents" position="replace"/>
</field>
</record>
<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">
<block id="website_info_settings" position="after">
<block title="Shop - Checkout Process" id="website_shop_checkout">
<setting id="cart_redirect_setting" string="Add to Cart" help="What should be done on &quot;Add to Cart&quot;?">
<div class="content-group">
<div class="row mt16 ms-4">
<field name="add_to_cart_action" widget="radio"/>
</div>
</div>
</setting>
<setting help="Instant checkout, instead of adding to cart">
<field name="enabled_buy_now_button"/>
</setting>
<setting help="Add a customizable form during checkout (after address)">
<field name="enabled_extra_checkout_step"/>
<div class="row mt8 ms-4" invisible="not enabled_extra_checkout_step">
<button type="object" name="action_open_extra_info" string="Configure Form " class="btn-link" icon="oi-arrow-right"/>
</div>
</setting>
<setting string="Assignment" help="Assignment of online orders">
<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" context="{'kanban_view_ref': 'sales_team.crm_team_view_kanban'}"/>
</div>
<div class="row">
<label class="o_light_label col-lg-3" for="salesperson_id"/>
<field name="salesperson_id"/>
</div>
</div>
</setting>
<setting help="Allow your customer to add products from previous order in their cart.">
<field name="website_sale_enabled_portal_reorder_button"/>
</setting>
</block>
<block title="Shop - Products" id="sale_product_catalog_settings">
<setting id="website_tax_inclusion_setting">
<label for="show_line_subtotals_tax_selection" string="Display Product Prices"/>
<span class="fa fa-lg fa-globe" title="Values set here are website-specific." groups="website.group_multi_website"/>
<div class="text-muted">
Prices displayed on your eCommerce
</div>
<div class="content-group">
<div class="row mt16">
<field name="show_line_subtotals_tax_selection" class="o_light_label" widget="radio"/>
</div>
</div>
</setting>
<setting 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." help="Manage pricelists to apply specific prices per country, customer, products, etc">
<field name="group_product_pricelist"/>
<div class="content-group mt16" invisible="not group_product_pricelist">
<field name="group_sale_pricelist" invisible="1"/>
<field name="group_product_pricelist" invisible="1"/>
<field name="product_pricelist_setting" class="o_light_label w-75" widget="radio"/>
</div>
<div invisible="not group_product_pricelist">
<button type="action" name="%(product.product_pricelist_action2)d" string="Pricelists" class="btn-link" icon="oi-arrow-right"/>
</div>
</setting>
<setting help="Add a strikethrough price, as a comparison">
<field name="group_product_price_comparison"/>
</setting>
<setting id="ecom_uom_price_option_setting" string="Product Reference Price" help="Add a reference price per UoM on products (i.e $/kg), in addition to the sale price">
<field name="group_show_uom_price"/>
</setting>
<setting id="product_attributes_setting" string="Product Variants" help="One product might have different attributes (size, color, ...)">
<field name="group_product_variant"/>
<div class="content-group" invisible="not group_product_variant">
<div class="mt8">
<button type="action" name="%(product.attribute_action)d" string="Attributes" class="btn-link" icon="oi-arrow-right"/>
</div>
</div>
</setting>
<setting id="promotion_coupon_programs" title="Boost your sales with multiple kinds of programs: Coupons, Promotions, Gift Card, Loyalty. Specific conditions can be set (products, customers, minimum purchase amount, period). Rewards can be discounts (% or amount) or free products." string="Discounts, Loyalty &amp; Gift Card" help="Manage Promotions, coupons, loyalty cards, Gift cards &amp; eWallet">
<field name="module_loyalty" />
</setting>
<setting id="wishlist_option_setting" help="Allow signed-in users to save product in a wishlist">
<field name="module_website_sale_wishlist"/>
</setting>
<setting id="comparator_option_setting" string="Product Comparison Tool" help="Allow shoppers to compare products based on their attributes">
<field name="module_website_sale_comparison"/>
</setting>
<setting id="hide_add_to_cart_setting" help="If product price equals 0, replace 'Add to Cart' by 'Contact us'.">
<field name="website_sale_prevent_zero_price_sale"/>
<div class="content-group" invisible="not website_sale_prevent_zero_price_sale">
<div class="row mt16">
<label class="o_light_label col-lg-3" string="Button url" for="website_sale_contact_us_button_url"/>
<field name="website_sale_contact_us_button_url"/>
</div>
</div>
</setting>
</block>
<block title="Shipping" id="sale_shipping_settings">
<setting id="shipping_address_setting" help="Let the customer enter a shipping address">
<field name="group_delivery_invoice_address"/>
</setting>
<setting id="delivery_method_setting" string="Shipping Costs" help="Compute shipping costs on orders"
documentation="/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html">
<div class="content-group">
<div class="mt16">
<button type="action" name="%(delivery.action_delivery_carrier_form)d" string="Shipping Methods" class="btn-link" icon="oi-arrow-right"/>
</div>
</div>
</setting>
<setting id="ups_provider_setting">
<div class="o_form_label">UPS</div>
<a href="https://www.odoo.com/documentation/17.0/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html" title="Documentation" class="o_doc_link" target="_blank"></a>
<div class="text-muted" id="website_delivery_ups">
Compute shipping costs and ship with UPS<br/>
<strong>(please go to Home>Apps to install)</strong>
</div>
</setting>
<setting id="shipping_provider_dhl_setting">
<div class="o_form_label">DHL</div>
<a href="https://www.odoo.com/documentation/17.0/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html" title="Documentation" class="o_doc_link" target="_blank"></a>
<div class="text-muted" id="website_delivery_dhl">
Compute shipping costs and ship with DHL<br/>
<strong>(please go to Home>Apps to install)</strong>
</div>
</setting>
<setting id="shipping_provider_fedex_setting">
<div class="o_form_label">FedEx</div>
<a href="https://www.odoo.com/documentation/17.0/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html" title="Documentation" class="o_doc_link" target="_blank"></a>
<div class="text-muted" id="website_delivery_fedex">
Compute shipping costs and ship with FedEx<br/>
<strong>(please go to Home>Apps to install)</strong>
</div>
</setting>
<setting id="shipping_provider_usps_setting">
<div class="o_form_label">USPS</div>
<a href="https://www.odoo.com/documentation/17.0/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html" title="Documentation" class="o_doc_link" target="_blank"></a>
<div class="text-muted" id="website_delivery_usps">
Compute shipping costs and ship with USPS<br/>
<strong>(please go to Home>Apps to install)</strong>
</div>
</setting>
<setting id="shipping_provider_bpost_setting" string="bpost" help="Compute shipping costs and ship with bpost"
documentation="/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html">
<field name="module_delivery_bpost" widget="upgrade_boolean"/>
<div class="content-group">
<div class="mt8" invisible="not module_delivery_bpost">
<button name="%(delivery.action_delivery_carrier_form)d" icon="oi-arrow-right" type="action" string="bpost Shipping Methods" class="btn-link" context="{'search_default_delivery_type': 'bpost'}"/>
</div>
</div>
</setting>
<setting id="shipping_provider_easypost_setting" string="Easypost" help="Compute shipping cost and ship with Easypost"
documentation="/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html">
<field name="module_delivery_easypost" widget="upgrade_boolean"/>
<div class="content-group">
<div class="mt8" invisible="not module_delivery_easypost">
<button name="%(delivery.action_delivery_carrier_form)d" icon="oi-arrow-right" type="action" string="Easypost Shipping Methods" class="btn-link" context="{'search_default_delivery_type': 'easypost'}"/>
</div>
</div>
</setting>
<setting
id="shipping_provider_shiprocket_setting"
string="Shiprocket"
help="Compute shipping cost and ship with Shiprocket"
documentation="/applications/inventory_and_mrp/inventory/shipping/setup/third_party_shipper.html"
>
<field name="module_delivery_shiprocket" widget="upgrade_boolean"/>
<div class="content-group">
<div class="mt8" invisible="not module_delivery_shiprocket">
<button
name="%(delivery.action_delivery_carrier_form)d"
icon="oi-arrow-right"
type="action"
string="Shiprocket Shipping Methods"
class="btn-link"
context="{'search_default_delivery_type': 'shiprocket'}"/>
</div>
</div>
</setting>
<setting id="shipping_provider_mondialrelay_setting" string="Mondial Relay" help="Let the customer select a Mondial Relay shipping point">
<field name="module_delivery_mondialrelay"/>
</setting>
<setting id="onsite_payment_setting" help="Allow customers to pay in person at your stores">
<field name="module_website_sale_picking"/>
</setting>
</block>
<field name='module_account' invisible="1"/>
<block title="Invoicing" id="sale_invoicing_settings" invisible="not module_account">
<setting id="invoicing_policy_setting" title="The mode selected here applies as invoicing policy of any new product created but not of products already existing." string="Invoicing Policy" help="Issue invoices to customers">
<div class="content-group">
<div class="mt16">
<field name="default_invoice_policy" class="o_light_label" widget="radio"/>
</div>
</div>
</setting>
<setting id="automatic_invoice_generation" invisible="default_invoice_policy == 'delivery'" help="Generate the invoice automatically when the online payment is confirmed">
<field name="automatic_invoice"/>
<div invisible="not automatic_invoice">
<label for="invoice_mail_template_id" class="o_light_label me-2"/>
<field name="invoice_mail_template_id" class="oe_inline"/>
</div>
</setting>
</block>
</block>
<setting id="cart_redirect_setting" position="after">
<setting id="website_checkout_registration" title=" To send invitations in B2B mode, open a contact or select several ones in list view and click on 'Portal Access Management' option in the dropdown menu *Action*."
string="Sign in/up at checkout" help="&quot;Optional&quot; allows guests to register from the order confirmation email to track their order.">
<field name="account_on_checkout" class="w-75" widget="radio"/>
</setting>
</setting>
<setting id="website_marketing_automation" position="after">
<setting
id="abandoned_carts_setting"
title="Customer needs to be signed in otherwise the mail address is not known.
&#10;&#10;- If a potential customer creates one or more abandoned checkouts and then completes a sale before the recovery email gets sent, then the email won't be sent.
&#10;&#10;- If user has manually sent a recovery email, the mail will not be sent a second time
&#10;&#10;- If a payment processing error occurred when the customer tried to complete their checkout, then the email won't be sent.
&#10;&#10;- If your shop does not support shipping to the customer's address, then the email won't be sent.
&#10;&#10;- If none of the products in the checkout are available for purchase (empty inventory, for example), then the email won't be sent.
&#10;&#10;- If all the products in the checkout are free, and the customer does not visit the shipping page to add a shipping fee or the shipping fee is also free, then the email won't be sent."
string="Automatically send abandoned checkout emails"
help="Mail only sent to signed in customers with items available for sale in their cart.">
<field name="send_abandoned_cart_email"/>
<div invisible="not send_abandoned_cart_email" 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="Send after" class="o_light_label"/>
<field class="col-2" name="cart_abandoned_delay" widget="float_time" /> Hours.
</div>
</div>
</div>
<div invisible="not send_abandoned_cart_email" class="mt8">
<button type="object" name="action_open_abandoned_cart_mail_template" string="Customize Abandoned Email Template" class="btn-link" icon="oi-arrow-right"/>
</div>
</setting>
</setting>
<setting id="google_analytics_setting" position="after">
<setting id="autocomplete_googleplaces_setting" help="Use Google Places API to validate addresses entered by your visitors">
<field name="module_website_sale_autocomplete"/>
</setting>
</setting>
</field>
</record>
</odoo>

View File

@ -0,0 +1,215 @@
<?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">
<filter name="my_sale_orders_filter" position="before">
<filter string="Confirmed" name="order_confirmed" domain="[('state', '=', 'sale')]"/>
<filter string="Unpaid" name="order_unpaid" domain="[('state', '=', 'sent')]"/>
<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'))]"/>
</filter>
</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">
<filter name="my_sale_orders_filter" position="attributes">
<attribute name="invisible">1</attribute>
</filter>
<filter name="my_sale_orders_filter" position="before">
<filter string="Order Date" name="order_date" date="date_order"/>
<separator/>
</filter>
</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">
<field name="team_id" position="after">
<field name="is_abandoned_cart" invisible="1"/>
<field name="cart_recovery_email_sent" invisible="1"/>
</field>
<button name="action_quotation_send" id="send_by_email_primary" position="attributes">
<attribute name="invisible">state != 'draft' or (is_abandoned_cart and not cart_recovery_email_sent)</attribute>
</button>
<button name="action_quotation_send" position="after">
<button name="action_recovery_email_send" type="object" id="send_recovery" data-hotkey="l"
string="Send a Recovery Email"
class="btn-primary"
invisible="not is_abandoned_cart or cart_recovery_email_sent"/>
</button>
<button name="action_quotation_send" id="send_by_email" position="after">
<button name="action_quotation_send" id="send_by_email_bis" string="Send by Email" type="object"
invisible="not is_abandoned_cart or cart_recovery_email_sent or state != 'draft'"/>
</button>
</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', '=', 'sale'), ('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="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>
<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" invisible="not website_id" groups="website.group_multi_website"/>
</field>
</field>
</record>
<record id="sale_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.sale_order_tree"/>
<field name="arch" type="xml">
<field name="user_id" position="before">
<field name="website_id" groups="website.group_multi_website" optional="show"/>
</field>
</field>
</record>
</odoo>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template name="Add to Cart Button" id="s_add_to_cart">
<div class="s_add_to_cart">
<button class="s_add_to_cart_btn disabled btn btn-secondary mb-2">
<i class="fa fa-cart-plus me-2"/>Add to Cart
</button>
</div>
</template>
<template id="s_add_to_cart_options" inherit_id="website.snippet_options">
<xpath expr="." position="inside">
<div data-js="AddToCart"
data-selector=".s_add_to_cart">
<we-row>
<we-many2one string="Product"
data-model="product.template"
data-set-product-template=""
data-name="product_template_picker_opt"
data-no-preview="true"
data-domain='[["is_published", "=", true], ["sale_ok", "=", true]]'
/>
<we-button data-name="product_template_reset_opt"
class="reset-product-picker align-self-end fa fa-fw fa-times">
</we-button>
</we-row>
<we-row>
<we-many2one-default-message string="Variant" class="o_we_sublevel_1"
data-model="product.product"
data-set-product-variant=""
data-name="product_variant_picker_opt"
data-no-preview="true"
data-default-message="Visitor's Choice"
/>
<we-button data-name="product_variant_reset_opt"
class="reset-variant-picker align-self-end fa fa-fw fa-times">
</we-button>
</we-row>
<we-select data-name="action_picker_opt" string="Action" data-no-preview="true">
<we-button data-set-action="add_to_cart">Add to Cart</we-button>
<we-button data-set-action="buy_now">Buy Now</we-button>
</we-select>
</div>
</xpath>
</template>
<asset id="website_sale.s_add_to_cart_000_js" name="Add to Cart 000 JS">
<bundle>web.assets_frontend</bundle>
<path>website_sale/static/src/snippets/s_add_to_cart/000.js</path>
</asset>
</odoo>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="s_dynamic_snippet_products" name="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'"/>
</t>
</xpath>
</template>
<template id="s_dynamic_snippet_products_template_options" inherit_id="website.s_dynamic_snippet_options_template">
<xpath expr="//we-select[@data-name='filter_opt']" position="after">
<t t-if="snippet_name == 'dynamic_snippet_products'">
<we-select string="Category" data-name="product_category_opt" data-attribute-name="productCategoryId" data-no-preview="true">
<we-button data-select-data-attribute="all">All Products</we-button>
<we-button data-select-data-attribute="current">Current Category or All</we-button>
</we-select>
<we-many2many string="Tags"
data-name="product_tag_opt"
data-attribute-name="productTagIds"
data-no-preview="true"
data-model="product.tag"
data-allow-delete="true"
data-fakem2m="true"
data-select-data-attribute=""/>
<we-input string="Product names" class="o_we_large" data-name="product_names_opt"
data-attribute-name="productNames" data-no-preview="true" data-select-data-attribute=""
placeholder="e.g. lamp,bin" title="Comma-separated list of parts of product names, barcodes or internal reference"/>
</t>
</xpath>
</template>
<asset id="website_sale.s_dynamic_snippet_products_000_js" name="Dynamic snippet products 000 JS">
<bundle>web.assets_frontend</bundle>
<path>website_sale/static/src/snippets/s_dynamic_snippet_products/000.js</path>
</asset>
</odoo>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<asset id="website_sale.s_popup_000_js" name="Popup 000 JS Website Sale Override">
<bundle>web.assets_frontend</bundle>
<path>website_sale/static/src/snippets/s_popup/000.js</path>
</asset>
</odoo>

View File

@ -0,0 +1,369 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<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" string="Products" t-thumbnail="/website_sale/static/src/img/snippets_thumbs/s_dynamic_products.svg"/>
</xpath>
<xpath expr="//t[@id='snippet_add_to_cart_hook']" position="replace">
<t t-snippet="website_sale.s_add_to_cart" string="Add to Cart Button" t-thumbnail="/website/static/src/img/snippets_thumbs/s_add_to_cart.svg"/>
</xpath>
</template>
<template id="snippet_options" inherit_id="website.snippet_options" name="e-commerce snippet options">
<xpath expr="." position="inside">
<!-- All products page -->
<div data-js="WebsiteSaleGridLayout" data-page-options="true" groups="website.group_website_designer" data-selector="main:has(.o_wsale_products_page)" data-no-check="true"
string="Products Page" data-target="#products_grid .o_wsale_products_grid_table_wrapper > table">
<we-select string="Layout" data-no-preview="true" data-reload="/">
<we-button data-customize-website-views="" data-name="grid_view_opt">Grid</we-button>
<we-button data-customize-website-views="website_sale.products_list_view">List</we-button>
</we-select>
<we-row string="Size" class="o_we_sublevel_1">
<we-input data-set-ppg="" data-step="1" data-no-preview="true" data-reload="/"/>
<span class="mx-2 o_wsale_ppr_by">by</span>
<we-select class="o_wsale_ppr_submenu" data-dependencies="grid_view_opt" data-no-preview="true" data-reload="/">
<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>
</we-row>
<we-select string="Style" class="o_we_sublevel_1">
<we-button data-select-class=""
data-customize-website-views="">
Default
</we-button>
<we-button data-select-class="o_wsale_design_cards"
data-customize-website-views="website_sale.products_design_card">
Cards
</we-button>
<we-button data-select-class="o_wsale_design_thumbs"
data-customize-website-views="website_sale.products_design_thumbs">
Thumbnails
</we-button>
<we-button data-select-class="o_wsale_design_grid"
data-customize-website-views="website_sale.products_design_grid">
Grid
</we-button>
</we-select>
<we-select string="Images Size" class="o_we_sublevel_1">
<we-button data-select-class="o_wsale_context_thumb_4_3"
data-customize-website-views="website_sale.products_thumb_4_3">
Landscape (4/3)
</we-button>
<we-button data-select-class=""
data-customize-website-views="">
Default (1/1)
</we-button>
<we-button data-select-class="o_wsale_context_thumb_4_5"
data-customize-website-views="website_sale.products_thumb_4_5">
Portrait (4/5)
</we-button>
<we-button data-select-class="o_wsale_context_thumb_2_3"
data-customize-website-views="website_sale.products_thumb_2_3">
Vertical (2/3)
</we-button>
</we-select>
<we-button-group string="Fill" class="o_we_sublevel_2" data-variable="thumb_size">
<we-button data-select-class=""
data-img="/website/static/src/img/snippets_options/content_width_normal.svg"
data-customize-website-views="">
</we-button>
<we-button data-select-class="o_wsale_context_thumb_cover"
data-name="thumb_cover"
data-variable="thumb_cover"
data-img="/website/static/src/img/snippets_options/content_width_full.svg"
data-customize-website-views="website_sale.products_thumb_cover">
</we-button>
</we-button-group>
<we-checkbox string="Search bar"
data-customize-website-views="website_sale.search"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Prod. Desc."
data-customize-website-views="website_sale.products_description"
data-no-preview="true"
data-reload="/"/>
<we-row id="o_wsale_grid_left_panel" string="Categories" data-variable="filmstrip">
<we-button string="Left"
data-customize-website-views="website_sale.products_categories"
data-name="categories_opt"
data-no-preview="true"
data-reload="/"/>
<we-button string="Top"
data-customize-website-views="website_sale.products_categories_top"
data-name="categories_opt_top"
data-no-preview="true"
data-reload="/"/>
</we-row>
<we-checkbox id="collapse_category_recursive" string="Collapse Category Recursive"
class="o_we_sublevel_1"
data-customize-website-views="website_sale.option_collapse_products_categories"
data-dependencies="categories_opt"
data-no-preview="true"
data-reload="/"/>
<we-row string="Attributes" class="o_we_full_row">
<we-button string="Left"
data-customize-website-views="website_sale.products_attributes"
data-name="attributes_opt"
data-no-preview="true"
data-reload="/"/>
<we-button string="Top"
data-customize-website-views="website_sale.products_attributes_top"
data-name="attributes_opt_top"
data-no-preview="true"
data-reload="/"/>
</we-row>
<we-checkbox string="Price Filter"
class="o_we_sublevel_1"
data-customize-website-views="website_sale.filter_products_price"
data-dependencies="attributes_opt, attributes_opt_top"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Product Tags Filter"
class="o_we_sublevel_1"
data-customize-website-views="website_sale.filter_products_tags"
data-dependencies="attributes_opt, attributes_opt_top"
data-no-preview="true"
data-reload="/"
/>
<we-row string="Top Bar" class="o_we_full_row">
<we-button string="Sort by"
data-customize-website-views="website_sale.sort"
data-no-preview="true"
data-reload="/"/>
<we-button string="Layout"
data-customize-website-views="website_sale.add_grid_or_list_option"
data-no-preview="true"
data-reload="/"/>
</we-row>
<we-select string="Default Sort" class="o_wsale_sort_submenu" data-no-preview="true" data-reload="/">
<t t-foreach="request.env['website']._get_product_sort_mapping()" t-as="query_and_label">
<we-button t-att-data-set-default-sort="query_and_label[0]"><t t-esc="query_and_label[1]"/></we-button>
</t>
</we-select>
<we-row string="Buttons" class="o_we_full_row">
<we-button title="Add to Cart" class="fa fa-fw fa-shopping-cart o_we_add_to_cart_btn"
data-customize-website-views="website_sale.products_add_to_cart"
data-no-preview="true"
data-reload="/"/>
</we-row>
</div>
<!-- Product -->
<div data-js="WebsiteSaleProductsItem"
data-selector="#products_grid .oe_product"
data-no-check="true">
<div class="o_wsale_soptions_menu_sizes">
<we-row string="Size">
<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>
</we-row>
</div>
<we-row string="Re-order" data-no-preview="true">
<we-button title="Push to top" data-change-sequence="top" class="fa fa-fw fa-angle-double-left"/>
<we-button title="Push up" data-change-sequence="up" class="fa fa-fw fa-angle-left"/>
<we-button title="Push down" data-change-sequence="down" class="fa fa-fw fa-angle-right"/>
<we-button title="Push to bottom" data-change-sequence="bottom" class="fa fa-fw fa-angle-double-right"/>
</we-row>
<we-row>
<we-select string="Badge" 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="" title="Edit" class="fa fa-edit" data-no-preview="true" data-dependencies="!no_ribbon_opt"/>
<we-button data-create-ribbon="" data-name="create_ribbon_opt" title="Create" class="fa fa-plus text-success" data-no-preview="true"/>
</we-row>
<div class="d-none" data-name="ribbon_customize_opt">
<we-input string="Content" class="o_we_sublevel_1 o_we_large"
data-set-ribbon-html="Badge Text" data-apply-to=".o_ribbon"/>
<we-colorpicker string="Background" class="o_we_sublevel_1"
title="" data-select-style="" data-css-property="background-color" data-color-prefix="text-bg-" data-apply-to=".o_ribbon"/>
<we-colorpicker string="Text" class="o_we_sublevel_1"
title="" data-select-style="" data-css-property="color" data-apply-to=".o_ribbon"/>
<we-select string="Style" class="o_we_sublevel_1">
<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" class="o_we_sublevel_1">
<we-button data-set-ribbon-position="left">Left</we-button>
<we-button data-set-ribbon-position="right">Right</we-button>
</we-select>
<we-row string=" ">
<we-button class="o_we_bg_danger" data-delete-ribbon="" data-no-preview="true">Delete Badge</we-button>
</we-row>
</div>
</div>
<div data-selector="#wrapwrap > header"
data-no-check="true"
groups="website.group_website_designer">
<we-row string="Show Empty" class="o_we_full_row">
<div class="d-flex gap-1 mb-1 w-100">
<we-button title="Show/hide shopping cart" class="o_btn_show_empty_cart fa fa-shopping-cart d-flex justify-content-center flex-grow-1"
data-customize-website-views="website_sale.header_hide_empty_cart_link|"
data-no-preview="true"
data-reload="/"/>
</div>
</we-row>
</div>
<!-- Product image -->
<div data-js="WebsiteSaleProductAttribute" data-selector="#product_detail .o_wsale_product_attribute" data-no-check="true">
<we-select string="Display Type" data-no-preview="true">
<we-button data-set-display-type="radio">Radio</we-button>
<we-button data-set-display-type="pills">Pills</we-button>
<we-button data-set-display-type="select">Select</we-button>
<we-button data-set-display-type="color">Color</we-button>
</we-select>
</div>
<!-- Product page -->
<div data-js="WebsiteSaleProductPage" data-selector="main:has(.o_wsale_product_page)" data-page-options="true" groups="website.group_website_designer" data-no-check="true" string="Product Page">
<we-row string="Customers" class="o_we_full_row">
<we-button string="Rating"
data-customize-website-views="website_sale.product_comment"
data-no-preview="true"
data-reload="/"/>
<we-button string="Share"
data-name="attributes_opt"
data-customize-website-views="website_sale.product_share_buttons"
data-no-preview="true"
data-reload="/"/>
</we-row>
<we-checkbox string="Select Quantity"
data-customize-website-views="website_sale.product_quantity"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Tax Indication"
data-customize-website-views="website_sale.tax_indication"
data-no-preview="true"
data-reload="/"/>
<we-select data-name="variants_opt" groups="product.group_product_variant" string="Variants" data-no-preview="true" data-reload="/">
<we-button data-name="variants_options_opt" data-customize-website-views="">Options</we-button>
<we-button data-name="variants_products_list_opt" data-customize-website-views="website_sale.product_variants">Products List</we-button>
</we-select>
<we-checkbox string="Product Tags"
data-customize-website-views="website_sale.product_tags"
data-no-preview="true"
data-reload="/"
/>
<we-row string="Cart" class="o_we_full_row" data-name="o_wsale_buy_now_opt">
<we-button title="Buy Now" class="o_we_buy_now_btn"
data-customize-website-views="website_sale.product_buy_now"
data-no-preview="true"
data-reload="/">
<i class="fa fa-fw fa-bolt"/>
Buy Now
</we-button>
</we-row>
<!-- Image config -->
<we-button-group string="Images Width" data-no-preview="true" data-reload="/">
<we-button data-set-image-width="none" data-img="/website_sale/static/src/img/snippet_options/image-width-none.svg" title="None"/>
<we-button data-set-image-width="50_pc" data-img="/website_sale/static/src/img/snippet_options/image-width-50.svg" title="50 percent"/>
<we-button data-set-image-width="66_pc" data-img="/website_sale/static/src/img/snippet_options/image-width-66.svg" title="66 percent"/>
<we-button data-set-image-width="100_pc" data-img="/website_sale/static/src/img/snippet_options/image-width-100.svg" title="100 percent"/>
</we-button-group>
<we-select string="Layout" data-name="o_wsale_image_layout" data-no-preview="true" data-reload="/">
<we-button data-set-image-layout="carousel">Carousel</we-button>
<we-button data-set-image-layout="grid">Grid</we-button>
</we-select>
<we-select string="Image Zoom" class="o_we_sublevel_1" data-name="o_wsale_zoom_mode" data-no-preview="true" data-reload="/">
<we-button data-name="o_wsale_zoom_hover" data-customize-website-views="website_sale.product_picture_magnify_hover">Magnifier on hover</we-button>
<we-button data-name="o_wsale_zoom_click" data-customize-website-views="website_sale.product_picture_magnify_click">Pop-up on Click</we-button>
<we-button data-name="o_wsale_zoom_both" data-customize-website-views="website_sale.product_picture_magnify_both">Both</we-button>
<we-button data-name="o_wsale_zoom_none" data-customize-website-views="">None</we-button>
</we-select>
<!-- Carousel config -->
<we-button-group string="Thumbnails" class="o_we_sublevel_1" data-name="o_wsale_thumbnail_pos" data-no-preview="true" data-reload="/">
<we-button class="fa fa-fw fa-long-arrow-left" title="Left" data-customize-website-views="website_sale.carousel_product_indicators_left"/>
<we-button class="fa fa-fw fa-long-arrow-down" title="Bottom" data-customize-website-views="website_sale.carousel_product_indicators_bottom"/>
</we-button-group>
<!-- Grid config -->
<we-range string="Image Spacing" class="o_we_sublevel_1" data-name="o_wsale_grid_spacing" data-no-preview="true" data-reload="/" data-max="3" data-step="1" data-set-spacing=""/>
<we-select string="Columns" class="o_we_sublevel_1" data-name="o_wsale_grid_columns" data-no-preview="true" data-reload="/">
<we-button data-set-columns="1">1</we-button>
<we-button data-set-columns="2">2</we-button>
<we-button data-set-columns="3">3</we-button>
</we-select>
<we-row string="Main image">
<we-button class="o_we_bg_success" data-name="o_wsale_replace_main_image" data-replace-main-image="true" data-no-preview="true">Replace</we-button>
</we-row>
<we-row string="Extra Images">
<we-button class="o_we_bg_success" data-name="o_wsale_add_extra_images" data-add-images="true" data-no-preview="true">Add</we-button>
<we-button class="o_we_bg_danger" data-name="o_wsale_clear_extra_images" data-clear-images="true" data-no-preview="true">Remove all</we-button>
</we-row>
</div>
<!-- Checkout page -->
<div data-selector="main:has(.oe_website_sale .o_wizard)" data-page-options="true" groups="website.group_website_designer" data-no-check="true" string="Checkout Pages">
<we-checkbox string="Extra Step"
data-customize-website-views="website_sale.extra_info"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Suggested Accessories"
data-customize-website-views="website_sale.suggested_products_list"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Promo Code"
data-customize-website-views="website_sale.reduction_code"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Accept Terms &amp; Conditions"
data-customize-website-views="website_sale.accept_terms_and_conditions"
data-no-preview="true"
data-reload="/"/>
<we-checkbox string="Show b2b Fields"
data-customize-website-views="website_sale.address_b2b"
data-no-preview="true"
data-reload="/"/>
</div>
</xpath>
</template>
<template id="snippets_options_web_editor" inherit_id="web_editor.snippet_options" name="e-commerce base snippet options">
<xpath expr="//div[@data-js='ReplaceMedia']" position="inside">
<we-row string="Re-order">
<we-button class="fa fa-fw fa-angle-double-left" data-no-preview="true" title="Move to first" data-set-position="first" data-name="media_wsale_resequence"/>
<we-button class="fa fa-fw fa-angle-left" data-no-preview="true" title="Move to previous" data-set-position="left" data-name="media_wsale_resequence"/>
<we-button class="fa fa-fw fa-angle-right" data-no-preview="true" title="Move to next" data-set-position="right" data-name="media_wsale_resequence"/>
<we-button class="fa fa-fw fa-angle-double-right" data-no-preview="true" title="Move to last" data-set-position="last" data-name="media_wsale_resequence"/>
</we-row>
</xpath>
<xpath expr="//div[@data-js='ReplaceMedia']/we-row" position="inside">
<we-button class="o_we_bg_danger" data-remove-media="true" data-no-preview="true" data-name="media_wsale_remove">Remove</we-button>
</xpath>
</template>
<template id="product_searchbar_input_snippet_options" inherit_id="website.searchbar_input_snippet_options" name="product search bar snippet options">
<xpath expr="//div[@data-js='SearchBar']/we-select[@data-name='scope_opt']" position="inside">
<we-button data-set-search-type="products" data-select-data-attribute="products" data-name="search_products_opt" data-form-action="/shop">Products</we-button>
</xpath>
<xpath expr="//div[@data-js='SearchBar']/we-select[@data-name='order_opt']" position="inside">
<t t-foreach="request.env['website']._get_product_sort_mapping()" t-as="query_and_label">
<!-- name asc is already part of the general sorting methods of this snippet. -->
<we-button t-if="query_and_label[0] != 'name asc'" t-att-data-set-order-by="query_and_label[0]" t-att-data-select-data-attribute="query_and_label[0]" data-dependencies="search_products_opt"><t t-out="query_and_label[1]"/></we-button>
</t>
</xpath>
<xpath expr="//div[@data-js='SearchBar']/div[@data-dependencies='limit_opt']" position="inside">
<we-checkbox string="Description" data-dependencies="search_products_opt" data-select-data-attribute="true" data-attribute-name="displayDescription"
data-apply-to=".search-query"/>
<we-checkbox string="Category" data-dependencies="search_products_opt" data-select-data-attribute="true" data-attribute-name="displayExtraLink"
data-apply-to=".search-query"/>
<we-checkbox string="Price" data-dependencies="search_products_opt" data-select-data-attribute="true" data-attribute-name="displayDetail"
data-apply-to=".search-query"/>
<we-checkbox string="Image" data-dependencies="search_products_opt" data-select-data-attribute="true" data-attribute-name="displayImage"
data-apply-to=".search-query"/>
</xpath>
</template>
</odoo>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="variants">
<t t-set="attribute_exclusions" t-value="product._get_attribute_exclusions(parent_combination)"/>
<ul t-attf-class="list-unstyled js_add_cart_variants mb-0 #{ul_class}" t-att-data-attribute_exclusions="json.dumps(attribute_exclusions)">
<t t-foreach="product.valid_product_template_attribute_line_ids" t-as="ptal">
<!-- Attributes selection is hidden if there is only one value available and it's not a custom value -->
<li t-att-data-attribute_id="ptal.attribute_id.id"
t-att-data-attribute_name="ptal.attribute_id.name"
t-att-data-attribute_display_type="ptal.attribute_id.display_type"
t-attf-class="variant_attribute #{
'd-none' if len(ptal.product_template_value_ids._only_active()) == 1
and not ptal.product_template_value_ids._only_active()[0].is_custom
and not ptal.attribute_id.display_type == 'multi' else ''
}">
<!-- Used to customize layout if the only available attribute value is custom -->
<t t-set="single" t-value="len(ptal.product_template_value_ids._only_active()) == 1"/>
<t t-set="single_and_custom" t-value="single and ptal.product_template_value_ids._only_active()[0].is_custom" />
<strong t-field="ptal.attribute_id.name" class="attribute_name"/>
<t t-if="ptal.attribute_id.display_type == 'select'">
<select
t-att-data-attribute_id="ptal.attribute_id.id"
t-attf-class="form-select css_attribute_select o_wsale_product_attribute js_variant_change #{ptal.attribute_id.create_variant} #{'d-none' if single_and_custom else ''}"
t-att-name="'ptal-%s' % ptal.id">
<t t-foreach="ptal.product_template_value_ids._only_active()" t-as="ptav">
<option t-att-value="ptav.id"
t-att-data-value_id="ptav.id"
t-att-data-value_name="ptav.name"
t-att-data-attribute_name="ptav.attribute_id.name"
t-att-data-is_custom="ptav.is_custom"
t-att-selected="ptav in combination"
t-att-data-is_single="single"
t-att-data-is_single_and_custom="single_and_custom">
<span t-field="ptav.name"/>
<t t-call="website_sale.badge_extra_price"/>
</option>
</t>
</select>
</t>
<t t-elif="ptal.attribute_id.display_type in ('radio', 'multi')">
<ul t-att-data-attribute_id="ptal.attribute_id.id" t-attf-class="list-inline list-unstyled o_wsale_product_attribute #{'d-none' if single_and_custom else ''}">
<t t-foreach="ptal.product_template_value_ids._only_active()" t-as="ptav">
<li class="list-inline-item mb-3 js_attribute_value" style="margin: 0;">
<label class="col-form-label">
<div class="form-check">
<input t-att-type="'radio' if ptal.attribute_id.display_type == 'radio' else 'checkbox'"
t-attf-class="form-check-input js_variant_change #{ptal.attribute_id.create_variant}"
t-att-checked="ptav in combination"
t-att-name="'ptal-%s' % ptal.id"
t-att-value="ptav.id"
t-att-data-value_id="ptav.id"
t-att-data-value_name="ptav.name"
t-att-data-attribute_name="ptav.attribute_id.name"
t-att-data-is_custom="ptav.is_custom"
t-att-data-is_single="single if ptal.attribute_id.display_type != 'multi' else False"
t-att-data-is_single_and_custom="single_and_custom"/>
<div class="radio_input_value form-check-label">
<span t-field="ptav.name"/>
<t t-call="website_sale.badge_extra_price"/>
</div>
</div>
</label>
</li>
</t>
</ul>
</t>
<t t-elif="ptal.attribute_id.display_type == 'pills'">
<ul t-att-data-attribute_id="ptal.attribute_id.id"
t-attf-class="btn-group-toggle list-inline list-unstyled o_wsale_product_attribute #{'d-none' if single_and_custom else ''}"
data-bs-toggle="buttons">
<t t-foreach="ptal.product_template_value_ids._only_active()" t-as="ptav">
<li t-attf-class="o_variant_pills btn btn-primary mb-1 list-inline-item js_attribute_value #{'active' if ptav in combination else ''}">
<input type="radio"
t-attf-class="js_variant_change #{ptal.attribute_id.create_variant}"
t-att-checked="ptav in combination"
t-att-name="'ptal-%s' % ptal.id"
t-att-value="ptav.id"
t-att-data-value_id="ptav.id"
t-att-id="ptav.id"
t-att-data-value_name="ptav.name"
t-att-data-attribute_name="ptav.attribute_id.name"
t-att-data-is_custom="ptav.is_custom"
t-att-data-is_single_and_custom="single_and_custom"
t-att-autocomplete="off"/>
<label class="radio_input_value o_variant_pills_input_value"
t-att-for="ptav.id">
<span t-field="ptav.name"/>
<t t-call="website_sale.badge_extra_price"/>
</label>
</li>
</t>
</ul>
</t>
<t t-elif="ptal.attribute_id.display_type == 'color'">
<ul t-att-data-attribute_id="ptal.attribute_id.id" t-attf-class="list-inline o_wsale_product_attribute #{'d-none' if single_and_custom else ''}">
<li t-foreach="ptal.product_template_value_ids._only_active()" t-as="ptav" class="list-inline-item me-1">
<t t-set="img_style"
t-value="'background:url(/web/image/product.template.attribute.value/%s/image); background-size:cover;' % ptav.id if ptav.image else ''"
/>
<t t-set="color_style"
t-value="'background:' + str(ptav.html_color or ptav.name if not ptav.is_custom else '')"
/>
<label t-attf-style="#{img_style or color_style}"
t-attf-class="css_attribute_color #{'active' if ptav in combination else ''} #{'custom_value' if ptav.is_custom else ''} #{'transparent' if (not ptav.is_custom and not ptav.html_color) else ''}"
>
<input type="radio"
t-attf-class="js_variant_change #{ptal.attribute_id.create_variant}"
t-att-checked="ptav in combination"
t-att-name="'ptal-%s' % ptal.id"
t-att-value="ptav.id"
t-att-title="ptav.name"
t-att-data-value_id="ptav.id"
t-att-data-value_name="ptav.name"
t-att-data-attribute_name="ptav.attribute_id.name"
t-att-data-is_custom="ptav.is_custom"
t-att-data-is_single="single"
t-att-data-is_single_and_custom="single_and_custom"/>
</label>
</li>
</ul>
</t>
</li>
</t>
</ul>
</template>
<template id="badge_extra_price" name="Badge Extra Price">
<t t-set="price_extra" t-value="ptav._get_extra_price(combination_info)"/>
<span t-if="price_extra" class="badge rounded-pill text-bg-light border">
<!--
price_extra is displayed as catalog price instead of
price after pricelist because it is impossible to
compute. Indeed, the pricelist rule might depend on the
selected variant, so the price_extra will be different
depending on the selected combination. The price of an
attribute is therefore variable and it's not very
accurate to display it.
-->
<span class="sign_badge_price_extra" t-out="price_extra > 0 and '+' or '-'"/>
<span t-out="abs(price_extra)"
class="variant_price_extra text-muted fst-italic"
style="white-space: nowrap;"
t-options='{
"widget": "monetary",
"display_currency": (pricelist or product.env.company).currency_id
}'/>
</span>
</template>
</odoo>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="base_unit_action" model="ir.actions.act_window">
<field name="name">Base Units</field>
<field name="res_model">website.base.unit</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

View File

@ -0,0 +1,87 @@
<?xml version="1.0"?>
<odoo>
<record id="product_pages_tree_view" model="ir.ui.view">
<field name="name">Product Pages Tree</field>
<field name="model">product.template</field>
<field name="priority">99</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="product_template_view_tree_website_sale"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="js_class">website_pages_list</attribute>
<attribute name="type">object</attribute>
<attribute name="action">open_website_url</attribute>
</xpath>
<field name="is_published" position="replace"/>
<field name="default_code" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="product_tag_ids" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="standard_price" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="name" position="after">
<field name="website_url"/>
</field>
<xpath expr="//tree">
<field name="is_seo_optimized"/>
<field name="is_published"/>
<field name="website_id" position="move"/>
</xpath>
</field>
</record>
<record id="product_pages_kanban_view" model="ir.ui.view">
<field name="name">Product Pages Kanban</field>
<field name="model">product.template</field>
<field name="priority">99</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="product_template_view_kanban_website_sale"/>
<field name="arch" type="xml">
<kanban position="attributes">
<attribute name="js_class">website_pages_kanban</attribute>
<attribute name="type">object</attribute>
<attribute name="action">open_website_url</attribute>
</kanban>
<kanban position="inside">
<field name="website_url"/>
</kanban>
<field name="name" position="after">
<div class="text-muted" t-if="record.website_id.value" groups="website.group_multi_website">
<i class="fa fa-globe me-1" title="Website"/>
<field name="website_id"/>
</div>
<div class="text-primary" t-esc="record.website_url.value"/>
</field>
<xpath expr="//div[hasclass('oe_kanban_details')]" position="inside">
<div class="o_kanban_footer">
<div class="position-absolute bottom-0 end-0 m-1">
<t t-if="record.is_published.raw_value">Published </t>
<t t-else="">Not Published </t>
<field name="is_published" widget="boolean_toggle"/>
</div>
</div>
</xpath>
</field>
</record>
<record id="action_product_pages_list" model="ir.actions.act_window">
<field name="name">Product Pages</field>
<field name="res_model">product.template</field>
<field name="view_mode">tree,kanban</field>
<field name="view_id" ref="product_pages_tree_view"/>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'sequence': 1, 'view_id': ref('product_pages_tree_view')}),
(0, 0, {'view_mode': 'kanban', 'sequence': 2, 'view_id': ref('product_pages_kanban_view')}),
]"/>
<field name="context">{'create_action': 'website_sale.product_product_action_add'}</field>
</record>
</odoo>

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="cart_delivery" name="Delivery Costs" inherit_id="website_sale.total">
<tr id="order_total_untaxed" position="before">
<tr id="order_delivery" t-if="website_sale_order and website_sale_order.carrier_id">
<td colspan="2" class="ps-0 pt-0 pb-2 border-0 text-muted"
title="Delivery will be updated after choosing a new delivery method">
Delivery
</td>
<td class="text-end pe-0 pt-0 pb-2 border-0 text-muted">
<span t-field="website_sale_order.amount_delivery"
class="monetary_field"
style="white-space: nowrap;"
t-options='{"widget": "monetary", "display_currency": website_sale_order.currency_id}'/>
</td>
</tr>
</tr>
</template>
<template id="payment_delivery_methods">
<input class="pe-none" t-att-value="delivery.id" t-att-id="'delivery_%i' % delivery.id" t-att-delivery_type="delivery.delivery_type" type="radio" name="delivery_type" t-att-checked="order.carrier_id and order.carrier_id.id == delivery.id and 'checked' or False"/>
<label class="label-optional" t-field="delivery.name"/>
<span class="o_wsale_delivery_badge_price float-end fw-bold" name="price">Select to compute delivery rate</span>
<t t-set='delivery_method' t-value="delivery.delivery_type+'_use_locations'" />
<div class="small">
<div class="d-none">
<span class="o_order_location">
<b class="o_order_location_name"/>
<br/>
<i class="o_order_location_address"/>
</span>
<span class="fa fa-times ms-2 o_remove_order_location" aria-label="Remove this location" title="Remove this location"/>
</div>
<t t-if="delivery_method in delivery.fields_get() and delivery[delivery_method]">
<div class="o_show_pickup_locations"/>
<div class="o_list_pickup_locations"/>
</t>
</div>
<t t-if="delivery.website_description">
<div t-field="delivery.website_description" class="text-muted mt8"/>
</t>
</template>
<template id="payment_delivery" name="Delivery Costs" inherit_id="website_sale.payment">
<!-- //t[@t-if='website_sale_order.amount_total'] should be removed in master -->
<xpath expr="//div[@name='website_sale_non_free_cart'] | //t[@name='website_sale_non_free_cart'] | //t[@t-if='website_sale_order.amount_total']" position="before">
<div t-if="deliveries" id="delivery_carrier">
<t t-set="delivery_nb" t-value="len(deliveries)"/>
<h4 class="fs-6 small text-uppercase fw-bolder">Choose a delivery method</h4>
<div class="card border-0" id="delivery_method">
<ul class="list-group">
<t t-foreach="deliveries" t-as="delivery">
<li class="list-group-item o_delivery_carrier_select">
<t t-call="website_sale.payment_delivery_methods"/>
</li>
</t>
</ul>
</div>
</div>
</xpath>
</template>
</odoo>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_delivery_carrier_form_website_delivery" model="ir.ui.view">
<field name="name">delivery.carrier.website.form</field>
<field name="model">delivery.carrier</field>
<field name="inherit_id" ref="delivery.view_delivery_carrier_form"/>
<field name="arch" type="xml">
<field name="company_id" position='after'>
<field name="website_id" groups="website.group_multi_website" options="{'no_open': True, 'no_create_edit': True}"/>
</field>
<field name="carrier_description" position='before'>
<field name="website_description" placeholder="Description displayed on the eCommerce and on online quotations."/>
</field>
<xpath expr="//button[@name='toggle_prod_environment']" position='before'>
<button name="website_publish_button" type="object" class="oe_stat_button" icon="fa-globe">
<field name="is_published" widget="website_publish_button"/>
</button>
</xpath>
</field>
</record>
<record id="view_delivery_carrier_tree" model="ir.ui.view">
<field name="name">delivery.carrier.tree.inherit</field>
<field name="model">delivery.carrier</field>
<field name="inherit_id" ref="delivery.view_delivery_carrier_tree"/>
<field name="arch" type="xml">
<field name="delivery_type" position="after">
<field name="is_published"/>
<field name="website_id" groups="website.group_multi_website"/>
</field>
</field>
</record>
<record id="view_delivery_carrier_search" model="ir.ui.view">
<field name="name">delivery.carrier.search.inherit</field>
<field name="model">delivery.carrier</field>
<field name="inherit_id" ref="delivery.view_delivery_carrier_search"/>
<field name="arch" type="xml">
<filter name="inactive" position="after">
<filter string="Published" name="is_published" domain="[('is_published','=',True)]"/>
</filter>
</field>
</record>
</odoo>

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<menuitem id="menu_ecommerce"
name="eCommerce"
parent="website.menu_website_configuration"
groups="sales_team.group_sale_salesman"
sequence="20">
<menuitem id="menu_orders"
name="Orders"
sequence="2">
<menuitem id="menu_orders_orders"
name="Orders"
action="action_orders_ecommerce"
sequence="1"/>
<menuitem id="menu_orders_unpaid_orders"
name="Unpaid Orders"
action="action_view_unpaid_quotation_tree"
sequence="2"/>
<menuitem id="menu_orders_abandoned_orders"
name="Abandoned Carts"
action="action_view_abandoned_tree"
sequence="3"/>
<menuitem id="menu_orders_customers"
name="Customers"
action="base.action_partner_customer_form"
sequence="4"/>
</menuitem>
<menuitem id="menu_catalog"
name="Products"
sequence="3">
<menuitem id="menu_catalog_products"
name="Products"
action="product_template_action_website"
sequence="1"/>
<menuitem id="menu_catalog_pricelists"
name="Pricelists"
action="product.product_pricelist_action2"
groups="product.group_product_pricelist"
sequence="3"/>
<menuitem id="menu_catalog_categories"
action="product_public_category_action"
sequence="4"/>
<menuitem id="menu_product_attribute_action"
action="product.attribute_action"
groups="product.group_product_variant"
sequence="5"/>
<menuitem id="product_catalog_product_tags"
name="Product Tags"
action="product.product_tag_action"/>
</menuitem>
</menuitem>
<menuitem id="menu_ecommerce_settings"
name="eCommerce"
parent="website.menu_website_global_configuration"
sequence="50">
<menuitem id="menu_ecommerce_payment_providers"
name="Payment Providers"
action="payment.action_payment_provider"
sequence="10"
/>
<menuitem id="menu_ecommerce_payment_methods"
action="payment.action_payment_method"
name="Payment Methods"
sequence="20"
/>
<menuitem id="menu_ecommerce_payment_tokens"
action="payment.action_payment_token"
groups="base.group_no_one"
sequence="30"
/>
<menuitem id="menu_ecommerce_payment_transactions"
action="payment.action_payment_transaction"
groups="base.group_no_one"
sequence="40"
/>
<menuitem id="menu_ecommerce_delivery"
action="delivery.action_delivery_carrier_form"
sequence="90"
/>
<menuitem id="menu_delivery_zip_prefix"
action="delivery.action_delivery_zip_prefix_list"
groups="base.group_no_one"
sequence="100"
/>
</menuitem>
<!-- Reporting sub-menus -->
<menuitem id="menu_report_sales" name="Online Sales"
action="sale_report_action_dashboard"
parent="website.menu_reporting"
groups="sales_team.group_sale_manager"
sequence="30"/>
<menuitem id="menu_product_pages"
name="Products"
action="action_product_pages_list"
parent="website.menu_content"
sequence="30"/>
</odoo>

View File

@ -0,0 +1,127 @@
<?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">
<field name="url" position="after">
<field name="product_id"/>
</field>
<filter name="type_url" position="after">
<filter string="Products" name="type_product" domain="[('product_id', '!=', False)]"/>
</filter>
<filter name="group_by_url" position="after">
<filter string="Product" name="group_by_product" domain="[]" context="{'group_by': 'product_id'}"/>
</filter>
</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" string="Products" 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">
<field name="page_ids" position="after">
<field name="product_ids" widget="many2many_tags" string="Products"/>
</field>
</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">
<field name="page_ids" position="after">
<field name="product_ids"/>
</field>
<xpath expr="//div[@id='o_page_count']" position="after">
<div id="o_product_count">Visited Products<span class="float-end fw-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">
<field name="url" position="after">
<field name="product_id"/>
</field>
</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">
<field name="url" position="after">
<field name="product_id"/>
</field>
</field>
</record>
</odoo>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<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">
<notebook position="inside">
<page string="Product Page Extra Fields" name="page_product_page_extra_fields" groups="base.group_no_one">
<field name="shop_extra_field_ids" context="{'default_website_id': id}">
<tree editable="bottom">
<field name="sequence" widget="handle"/>
<field name="field_id" required="1" options="{'no_create': True}"/>
</tree>
</field>
</page>
</notebook>
</field>
</record>
</odoo>