[IMP] website_sale: add price per unit on products
Description of the issue/feature this PR addresses: Currently, we cannot display prices per unit (for example $/kg, etc.) on products shown in the shop. This is important for any eCommerce business that sells something other than units, or just for informational purposes. Desired behavior after PR is merged: Allows the user to add unit measurements for products. This behaviour is disabled by default, and can be enabled through the settings page by activating "eCommerce Base Unit Price" option. Enabling it displays a price per unit field in /shop's product page, comparison tool and cart. The price per unit of measurement is automatically computed and can be displayed both on the product, the cart and the comparison pages. Enabling it also modifies the product's configuration page to include what unit of measure to display in the price per unit field, as well as the number of units used to calculate the price per unit. task-2558128 Part-of: odoo/odoo#72242 Co-authored-by: Kamen Zhekov <kzh@odoo.com> Co-authored-by: Jeremy Kersten <jke@odoo.com>
This commit is contained in:
committed by
Jeremy Kersten
co-authored by
Jeremy Kersten
parent
e0f0b86a9b
commit
8fc2e229f9
@@ -642,11 +642,7 @@ class ProductTemplate(models.Model):
|
||||
if combination in existing_variants:
|
||||
current_variants_to_activate += existing_variants[combination]
|
||||
else:
|
||||
current_variants_to_create.append({
|
||||
'product_tmpl_id': tmpl_id.id,
|
||||
'product_template_attribute_value_ids': [(6, 0, combination.ids)],
|
||||
'active': tmpl_id.active,
|
||||
})
|
||||
current_variants_to_create.append(tmpl_id._prepare_variant_values(combination))
|
||||
if len(current_variants_to_create) > 1000:
|
||||
raise UserError(_(
|
||||
'The number of variants to generate is too high. '
|
||||
@@ -682,6 +678,14 @@ class ProductTemplate(models.Model):
|
||||
self.invalidate_cache()
|
||||
return True
|
||||
|
||||
def _prepare_variant_values(self, combination):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'product_tmpl_id': self.id,
|
||||
'product_template_attribute_value_ids': [(6, 0, combination.ids)],
|
||||
'active': self.active
|
||||
}
|
||||
|
||||
def has_dynamic_attributes(self):
|
||||
"""Return whether this `product.template` has at least one dynamic
|
||||
attribute.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/digest_views.xml',
|
||||
'views/website_sale_visitor_views.xml',
|
||||
'views/base_unit_view.xml'
|
||||
],
|
||||
'demo': [
|
||||
'data/demo.xml',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_move
|
||||
from . import base_unit
|
||||
from . import crm_team
|
||||
from . import ir_http
|
||||
from . import mail_compose_message
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class BaseUnit(models.Model):
|
||||
_name = "website.base.unit"
|
||||
_description = "Unit of Measure for price per unit on eCommerce products."
|
||||
_order = "name"
|
||||
|
||||
name = fields.Char(help="Define a custom unit to display in the price per unit of measure field.",
|
||||
required=True, translate=True)
|
||||
@@ -207,6 +207,52 @@ class ProductTemplate(models.Model):
|
||||
|
||||
product_template_image_ids = fields.One2many('product.image', 'product_tmpl_id', string="Extra Product Media", copy=True)
|
||||
|
||||
base_unit_count = fields.Float('Base Unit Count', required=True, default=0,
|
||||
compute='_compute_base_unit_count', inverse='_set_base_unit_count', store=True,
|
||||
help="Display base unit price on your eCommerce pages. Set to 0 to hide it for this product.")
|
||||
base_unit_id = fields.Many2one('website.base.unit', string='Custom Unit of Measure',
|
||||
compute='_compute_base_unit_id', inverse='_set_base_unit_id', store=True,
|
||||
help="Define a custom unit to display in the price per unit of measure field.")
|
||||
base_unit_price = fields.Monetary("Price Per Unit", currency_field="currency_id", compute="_compute_base_unit_price")
|
||||
base_unit_name = fields.Char(compute='_compute_base_unit_name', help='Displays the custom unit for the products if defined or the selected unit of measure otherwise.')
|
||||
|
||||
@api.depends('product_variant_ids', 'product_variant_ids.base_unit_count')
|
||||
def _compute_base_unit_count(self):
|
||||
self.base_unit_count = 0
|
||||
for template in self.filtered(lambda template: len(template.product_variant_ids) == 1):
|
||||
template.base_unit_count = template.product_variant_ids.base_unit_count
|
||||
|
||||
def _set_base_unit_count(self):
|
||||
for template in self:
|
||||
if len(template.product_variant_ids) == 1:
|
||||
template.product_variant_ids.base_unit_count = template.base_unit_count
|
||||
|
||||
@api.depends('product_variant_ids', 'product_variant_ids.base_unit_count')
|
||||
def _compute_base_unit_id(self):
|
||||
self.base_unit_id = self.env['website.base.unit']
|
||||
for template in self.filtered(lambda template: len(template.product_variant_ids) == 1):
|
||||
template.base_unit_id = template.product_variant_ids.base_unit_id
|
||||
|
||||
def _set_base_unit_id(self):
|
||||
for template in self:
|
||||
if len(template.product_variant_ids) == 1:
|
||||
template.product_variant_ids.base_unit_id = template.base_unit_id
|
||||
|
||||
@api.depends('price', 'lst_price', 'base_unit_count')
|
||||
def _compute_base_unit_price(self):
|
||||
for template in self:
|
||||
template.base_unit_price = template.base_unit_count and (template.price or template.lst_price) / template.base_unit_count
|
||||
|
||||
@api.depends('uom_name', 'base_unit_id.name')
|
||||
def _compute_base_unit_name(self):
|
||||
for template in self:
|
||||
template.base_unit_name = template.base_unit_id.name or template.uom_name
|
||||
|
||||
def _prepare_variant_values(self, combination):
|
||||
variant_dict = super()._prepare_variant_values(combination)
|
||||
variant_dict['base_unit_count'] = self.base_unit_count
|
||||
return variant_dict
|
||||
|
||||
def _get_website_accessory_product(self):
|
||||
domain = self.env['website'].sale_product_domain()
|
||||
return self.accessory_product_ids.filtered_domain(domain)
|
||||
@@ -294,9 +340,14 @@ class ProductTemplate(models.Model):
|
||||
parent_combination=parent_combination, only_template=only_template)
|
||||
|
||||
if self.env.context.get('website_id'):
|
||||
context = dict(self.env.context, ** {
|
||||
'quantity': self.env.context.get('quantity', add_qty),
|
||||
'pricelist': pricelist and pricelist.id
|
||||
})
|
||||
|
||||
product = (self.env['product.product'].browse(combination_info['product_id']) or self).with_context(context)
|
||||
partner = self.env.user.partner_id
|
||||
company_id = current_website.company_id
|
||||
product = self.env['product.product'].browse(combination_info['product_id']) or self
|
||||
|
||||
tax_display = self.user_has_groups('account.group_show_line_subtotals_tax_excluded') and 'total_excluded' or 'total_included'
|
||||
fpos = self.env['account.fiscal.position'].sudo().get_fiscal_position(partner.id)
|
||||
@@ -317,6 +368,8 @@ class ProductTemplate(models.Model):
|
||||
has_discounted_price = pricelist.currency_id.compare_amounts(list_price, price) == 1
|
||||
|
||||
combination_info.update(
|
||||
base_unit_name=product.base_unit_name,
|
||||
base_unit_price=product.base_unit_price,
|
||||
price=price,
|
||||
list_price=list_price,
|
||||
has_discounted_price=has_discounted_price,
|
||||
@@ -451,6 +504,29 @@ class Product(models.Model):
|
||||
|
||||
website_url = fields.Char('Website URL', compute='_compute_product_website_url', help='The full URL to access the document through the website.')
|
||||
|
||||
base_unit_count = fields.Float('Base Unit Count', required=True, default=1, help="Display base unit price on your eCommerce pages. Set to 0 to hide it for this product.")
|
||||
base_unit_id = fields.Many2one('website.base.unit', string='Custom Unit of Measure', help="Define a custom unit to display in the price per unit of measure field.")
|
||||
base_unit_price = fields.Monetary("Price Per Unit", currency_field="currency_id", compute="_compute_base_unit_price")
|
||||
base_unit_name = fields.Char(compute='_compute_base_unit_name', help='Displays the custom unit for the products if defined or the selected unit of measure otherwise.')
|
||||
|
||||
@api.depends('price', 'lst_price', 'base_unit_count')
|
||||
def _compute_base_unit_price(self):
|
||||
for product in self:
|
||||
if not product.id:
|
||||
product.base_unit_price = 0
|
||||
else:
|
||||
product.base_unit_price = product.base_unit_count and (product.price or product.lst_price) / product.base_unit_count
|
||||
|
||||
@api.depends('uom_name', 'base_unit_id')
|
||||
def _compute_base_unit_name(self):
|
||||
for product in self:
|
||||
product.base_unit_name = product.base_unit_id.name or product.uom_name
|
||||
|
||||
@api.constrains('base_unit_count')
|
||||
def _check_base_unit_count(self):
|
||||
if any(product.base_unit_count < 0 for product in self):
|
||||
raise ValidationError(_('The value of Base Unit Count must be greater than 0. Use 0 to hide the price per unit on this product.'))
|
||||
|
||||
@api.depends_context('lang')
|
||||
@api.depends('product_tmpl_id.website_url', 'product_template_attribute_value_ids')
|
||||
def _compute_product_website_url(self):
|
||||
@@ -458,6 +534,11 @@ class Product(models.Model):
|
||||
attributes = ','.join(str(x) for x in product.product_template_attribute_value_ids.ids)
|
||||
product.website_url = "%s#attr=%s" % (product.product_tmpl_id.website_url, attributes)
|
||||
|
||||
def _prepare_variant_values(self, combination):
|
||||
variant_dict = super()._prepare_variant_values(combination)
|
||||
variant_dict['base_unit_count'] = self.base_unit_count
|
||||
return variant_dict
|
||||
|
||||
def website_publish_button(self):
|
||||
self.ensure_one()
|
||||
return self.product_tmpl_id.website_publish_button()
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
_inherit = "product.product"
|
||||
|
||||
website_id = fields.Many2one(related='product_tmpl_id.website_id', readonly=False)
|
||||
|
||||
product_variant_image_ids = fields.One2many('product.image', 'product_variant_id', string="Extra Variant Images")
|
||||
|
||||
website_url = fields.Char('Website URL', compute='_compute_product_website_url', help='The full URL to access the document through the website.')
|
||||
|
||||
base_unit_count = fields.Float('Base Unit Count', required=True, default=1, help="Display base unit price on your eCommerce pages. Set to 0 to hide it for this product.")
|
||||
base_unit_id = fields.Many2one('website.base.unit', string='Custom Unit of Measure', help="Define a custom unit to display in the price per unit of measure field.")
|
||||
base_unit_price = fields.Monetary("Price Per Unit", currency_field="currency_id", compute="_compute_base_unit_price")
|
||||
base_unit_name = fields.Char(compute='_compute_base_unit_name', help='Displays the custom unit for the products if defined or the selected unit of measure otherwise.')
|
||||
|
||||
@api.depends('price', 'lst_price', 'base_unit_count')
|
||||
def _compute_base_unit_price(self):
|
||||
for product in self:
|
||||
if not product.id:
|
||||
product.base_unit_price = 0
|
||||
else:
|
||||
product.base_unit_price = product.base_unit_count and (product.price or product.lst_price) / product.base_unit_count
|
||||
|
||||
@api.depends('uom_name', 'base_unit_id')
|
||||
def _compute_base_unit_name(self):
|
||||
for product in self:
|
||||
product.base_unit_name = product.base_unit_id.name or product.uom_name
|
||||
|
||||
@api.constrains('base_unit_count')
|
||||
def _check_base_unit_count(self):
|
||||
if any(product.base_unit_count < 0 for product in self):
|
||||
raise ValidationError(_('The value of Base Unit Count must be greater than 0. Use 0 to hide the price per unit on this product.'))
|
||||
|
||||
@api.depends_context('lang')
|
||||
@api.depends('product_tmpl_id.website_url', 'product_template_attribute_value_ids')
|
||||
def _compute_product_website_url(self):
|
||||
for product in self:
|
||||
attributes = ','.join(str(x) for x in product.product_template_attribute_value_ids.ids)
|
||||
product.website_url = "%s#attr=%s" % (product.product_tmpl_id.website_url, attributes)
|
||||
|
||||
def _prepare_variant_values(self, combination):
|
||||
variant_dict = super()._prepare_variant_values(combination)
|
||||
variant_dict['base_unit_count'] = self.base_unit_count
|
||||
return variant_dict
|
||||
|
||||
def website_publish_button(self):
|
||||
self.ensure_one()
|
||||
return self.product_tmpl_id.website_publish_button()
|
||||
|
||||
def open_website_url(self):
|
||||
self.ensure_one()
|
||||
res = self.product_tmpl_id.open_website_url()
|
||||
res['url'] = self.website_url
|
||||
return res
|
||||
|
||||
def _get_images(self):
|
||||
"""Return a list of records implementing `image.mixin` to
|
||||
display on the carousel on the website for this variant.
|
||||
|
||||
This returns a list and not a recordset because the records might be
|
||||
from different models (template, variant and image).
|
||||
|
||||
It contains in this order: the main image of the variant (if set), the
|
||||
Variant Extra Images, and the Template Extra Images.
|
||||
"""
|
||||
self.ensure_one()
|
||||
variant_images = list(self.product_variant_image_ids)
|
||||
if self.image_variant_1920:
|
||||
# if the main variant image is set, display it first
|
||||
variant_images = [self] + variant_images
|
||||
else:
|
||||
# If the main variant image is empty, it will fallback to template
|
||||
# image, in this case insert it after the other variant images, so
|
||||
# that all variant images are first and all template images last.
|
||||
variant_images = variant_images + [self]
|
||||
# [1:] to remove the main image from the template, we only display
|
||||
# the template extra images here
|
||||
return variant_images + self.product_tmpl_id._get_images()[1:]
|
||||
|
||||
def _is_sold_out(self):
|
||||
combination_info = self.with_context(website_sale_stock_get_quantity=True).product_tmpl_id._get_combination_info(product_id=self.id)
|
||||
return combination_info['product_type'] == 'product' and combination_info['free_qty'] <= 0
|
||||
@@ -18,6 +18,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
], string="Shipping Management")
|
||||
|
||||
group_delivery_invoice_address = fields.Boolean(string="Shipping Address", implied_group='sale.group_delivery_invoice_address', group='base.group_portal,base.group_user,base.group_public')
|
||||
group_show_uom_price = fields.Boolean(default=False, string="Base Unit Price", implied_group="website_sale.group_show_uom_price", group='base.group_portal,base.group_user,base.group_public')
|
||||
|
||||
module_website_sale_digital = fields.Boolean("Digital Content")
|
||||
module_website_sale_wishlist = fields.Boolean("Wishlists")
|
||||
|
||||
@@ -23,3 +23,5 @@ access_product_image_sale,product.image sale,model_product_image,sales_team.grou
|
||||
access_website_sale_payment_acquirer_onboarding_wizard,access.website.sale.payment.acquirer.onboarding.wizard,model_website_sale_payment_acquirer_onboarding_wizard,base.group_system,1,1,1,0
|
||||
access_ecom_extra_fields_public,access_ecom_extra_field public,model_website_sale_extra_field,,1,0,0,0
|
||||
access_ecom_extra_fields_publisher,access_ecom_extra_field publisher,model_website_sale_extra_field,website.group_website_publisher,1,1,1,1
|
||||
access_website_base_unit_public,website.base.unit public,model_website_base_unit,,1,0,0,0
|
||||
access_website_base_unit_sale_manager,website.base.unit sale manager,model_website_base_unit,sales_team.group_sale_manager,1,1,1,1
|
||||
|
||||
|
@@ -12,6 +12,11 @@
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="group_show_uom_price" model="res.groups">
|
||||
<field name="name">UOM Price Display for eCommerce</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="sales_team.group_sale_manager" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('website.group_website_publisher'))]"/>
|
||||
</record>
|
||||
|
||||
@@ -11,12 +11,26 @@ var VariantMixin = require('sale.VariantMixin');
|
||||
* @param {string} uri The uri to adapt
|
||||
*/
|
||||
VariantMixin._getUri = function (uri) {
|
||||
if (this.isWebsite){
|
||||
if (this.isWebsite) {
|
||||
return uri + '_website';
|
||||
} else {
|
||||
return uri;
|
||||
}
|
||||
};
|
||||
const originalOnChangeCombination = VariantMixin._onChangeCombination;
|
||||
VariantMixin._onChangeCombination = function (ev, $parent, combination) {
|
||||
const $pricePerUom = $parent.find(".o_base_unit_price:first .oe_currency_value");
|
||||
if ($pricePerUom) {
|
||||
if (combination.is_combination_possible !== false && combination.base_unit_price != 0) {
|
||||
$pricePerUom.parents(".o_base_unit_price_wrapper").removeClass("d-none");
|
||||
$pricePerUom.text(this._priceToStr(combination.base_unit_price));
|
||||
$parent.find(".oe_custom_base_unit:first").text(combination.base_unit_name);
|
||||
} else {
|
||||
$pricePerUom.parents(".o_base_unit_price_wrapper").addClass("d-none");
|
||||
}
|
||||
}
|
||||
originalOnChangeCombination.apply(this, [ev, $parent, combination]);
|
||||
};
|
||||
|
||||
const originalToggleDisable = VariantMixin._toggleDisable;
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
|
||||
<menuitem name="Base Units" id="website_base_unit_menu" action="base_unit_action"
|
||||
parent="website_sale.menu_product_settings" sequence="5" groups="website_sale.group_show_uom_price"/>
|
||||
|
||||
</odoo>
|
||||
@@ -98,6 +98,43 @@
|
||||
</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">
|
||||
<xpath expr="//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="o_row">
|
||||
<field name="base_unit_count" attrs="{'invisible': [('product_variant_count', '>', 1)]}" />
|
||||
<field name="base_unit_id" options="{'no_open': True}" attrs="{'invisible': [('product_variant_count', '>', 1)]}" />
|
||||
<span attrs="{'invisible': ['|', ('base_unit_price','=', 0), ('product_variant_count', '>', 1)]}">
|
||||
(<field name="base_unit_price"/> / <field name="base_unit_name"/>)
|
||||
</span>
|
||||
<span class='text-muted' attrs="{'invisible': [('product_variant_count', '<=', 1)]}">Based on variants</span>
|
||||
</div>
|
||||
</xpath>
|
||||
</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">
|
||||
<xpath expr="//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="o_row">
|
||||
<field name="base_unit_count"/>
|
||||
<field name="base_unit_id" options="{'no_open': True}"/>
|
||||
<span attrs="{'invisible': [('base_unit_price','=', 0)]}">
|
||||
(<field name="base_unit_price"/> / <field name="base_unit_name"/>)
|
||||
</span>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view">
|
||||
<field name="name">product.template.product.website.form</field>
|
||||
<field name="model">product.template</field>
|
||||
@@ -133,6 +170,16 @@
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_variant_easy_edit_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="standard_price" 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="o_row">
|
||||
<field name="base_unit_count"/>
|
||||
<field name="base_unit_id" options="{'no_open': True}"/>
|
||||
<span attrs="{'invisible': [('base_unit_price','=', 0)]}">
|
||||
(<field name="base_unit_price"/> / <field name="base_unit_name"/>)
|
||||
</span>
|
||||
</div>
|
||||
</field>
|
||||
<sheet position="inside">
|
||||
<group name="product_variant_images" string="Extra Variant Media">
|
||||
<field name="product_variant_image_ids" class="o_website_sale_image_list" context="{'default_name': name}" mode="kanban" add-label="Add a Media" nolabel="1"/>
|
||||
|
||||
@@ -101,6 +101,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="ecom_uom_price_option_setting">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_show_uom_price"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="group_show_uom_price"/>
|
||||
<div class="text-muted">
|
||||
Add price per base unit of measure on your Products
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Pricing</h2>
|
||||
|
||||
@@ -571,6 +571,11 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="base_unit_price" name="Product Bas eunit price">
|
||||
(<span class="o_base_unit_price" t-field="product.base_unit_price" t-options="{'display_currency': website.currency_id}"/>
|
||||
/ <span class="oe_custom_base_unit" t-field="product.base_unit_name"/>)
|
||||
</template>
|
||||
|
||||
<template id="product" name="Product" track="1">
|
||||
|
||||
<t t-set="combination" t-value="product._get_first_possible_combination()"/>
|
||||
@@ -629,7 +634,12 @@
|
||||
<form t-if="product._is_add_to_cart_possible()" action="/shop/cart/update" method="POST">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||
<div class="js_product js_main_product mb-3">
|
||||
<t t-call="website_sale.product_price" />
|
||||
<div>
|
||||
<t t-call="website_sale.product_price" />
|
||||
<small class="ml-1 text-muted o_base_unit_price_wrapper d-none" groups="website_sale.group_show_uom_price">
|
||||
<t t-call='website_sale.base_unit_price'/>
|
||||
</small>
|
||||
</div>
|
||||
<t t-placeholder="select">
|
||||
<input type="hidden" class="product_id" name="product_id" t-att-value="product_variant.id" />
|
||||
<input type="hidden" class="product_template_id" name="product_template_id" t-att-value="product.id" />
|
||||
@@ -787,7 +797,7 @@
|
||||
</template>
|
||||
|
||||
<template id="product_price">
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt-2 mb-3">
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price d-inline-block mt-2 mb-3">
|
||||
<h3 class="css_editable_mode_hidden">
|
||||
<span class="oe_price" style="white-space: nowrap;" t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<span itemprop="price" style="display:none;" t-esc="combination_info['price']"/>
|
||||
@@ -1054,7 +1064,7 @@
|
||||
</td>
|
||||
<td class="text-center td-price" name="price">
|
||||
<t t-set="combination" t-value="line.product_id.product_template_attribute_value_ids + line.product_no_variant_attribute_value_ids"/>
|
||||
<t t-set="combination_info" t-value="line.product_id.product_tmpl_id._get_combination_info(combination, pricelist=website_sale_order.pricelist_id)"/>
|
||||
<t t-set="combination_info" t-value="line.product_id.product_tmpl_id._get_combination_info(combination, pricelist=website_sale_order.pricelist_id, add_qty=line.product_uom_qty)"/>
|
||||
|
||||
<t t-set="list_price_converted" t-value="website.currency_id._convert(combination_info['list_price'], website_sale_order.currency_id, website_sale_order.company_id, date)"/>
|
||||
<t groups="account.group_show_line_subtotals_tax_excluded" t-if="(website_sale_order.pricelist_id.discount_policy == 'without_discount' and website_sale_order.currency_id.compare_amounts(list_price_converted, line.price_reduce_taxexcl) == 1) or website_sale_order.currency_id.compare_amounts(line.price_unit, line.price_reduce) == 1" name="order_line_discount">
|
||||
@@ -1065,6 +1075,9 @@
|
||||
<del t-attf-class="#{'text-danger mr8'}" style="white-space: nowrap;" t-esc="list_price_converted" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" />
|
||||
</t>
|
||||
<span t-field="line.price_reduce_taxinc" style="white-space: nowrap;" t-options="{'widget': 'monetary', 'display_currency': website_sale_order.currency_id}" groups="account.group_show_line_subtotals_tax_included" />
|
||||
<small t-if="not line._is_not_sellable_line() and line.product_id.base_unit_price" class="cart_product_base_unit_price d-block text-muted" groups="website_sale.group_show_uom_price">
|
||||
<t t-call='website_sale.base_unit_price'><t t-set='product' t-value='line.product_id' /></t>
|
||||
</small>
|
||||
</td>
|
||||
<td class="td-action">
|
||||
<a href='#' aria-label="Remove from cart" title="Remove from cart" class='js_delete_product no-decoration'> <small><i class='fa fa-trash-o'></i></small></a>
|
||||
|
||||
@@ -129,6 +129,9 @@
|
||||
<strong>Price:</strong>
|
||||
<del t-attf-class="text-danger mr8 {{'' if combination_info['has_discounted_price'] else 'd-none'}}" style="white-space: nowrap;" t-esc="combination_info['list_price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}" />
|
||||
<span t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"></span>
|
||||
<small class="d-block text-muted" groups="website_sale.group_show_uom_price" t-if="product.base_unit_price">
|
||||
<t t-call='website_sale.base_unit_price'><t t-set='product' t-value='line.product_id' /></t>
|
||||
</small>
|
||||
</span>
|
||||
|
||||
<form action="/shop/cart/update" method="post" class="text-center">
|
||||
|
||||
@@ -187,6 +187,9 @@
|
||||
<td class="align-middle">
|
||||
<t t-set="combination_info" t-value="wish.product_id._get_combination_info_variant()"/>
|
||||
<t t-esc="combination_info['price']" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
<small t-if="wish.product_id.base_unit_price" class="cart_product_base_unit_price d-block text-muted" groups="website_sale.group_show_uom_price">
|
||||
<t t-call='website_sale.base_unit_price'><t t-set='product' t-value='wish.product_id' /></t>
|
||||
</small>
|
||||
</td>
|
||||
<td class='text-center td-wish-btn align-middle'>
|
||||
<input name="product_id" t-att-value="wish.product_id.id" type="hidden"/>
|
||||
|
||||
Reference in New Issue
Block a user