[IMP] various: add event and ticket name on sale order and invoice line
In event_sale it is possible to have one product representing several tickets. But before this commit, on the sale order and invoice the client only saw the name of the product and didn't know which event/ticket they subscribed to. The goal of this commit is to overwrite that behavior whenever a sale order or invoice line is related to an event ticket, to show the ticket name and event name instead of the product name. Technically, the following changes have been done: ---------------------------------------- * various: add a method to compute a default name for a SaleOrderLine, based on its product I added the method because the code to compute the name was duplicated in several places. This will make it more consistent. Moreover this gives the option for modules to override the method to define a custom name as necessary. ---------------------------------------- * event_sale: compute a consistent description for a SaleOrderLine based on an EventTicket The description will be the same whether the sale order comes from the front end or the back end. It takes into account the customer language. The default description will be computed as follow: 1. The ticket name should be the first line of the product description on the SO. 2. The event name should be the second line of the product description on the SO. To compute the ticket multi-line description we decided to ignore entirely the product name and the product description_sale because they are considered to be replaced by the ticket name and event name. We also ignore them because the work-flow of creating a new event does not lead to filling them correctly, as the product is created through the event interface. ---------------------------------------- * event_sale: improve sale order line and event registration views It was possible to select a ticket related to a product before an event was selected. This is typically the case for tickets defined in an event_type but not in an event. The ticket field is now hidden until both the product and the event have been selected. This way we are sure the selected ticket will belong to an event and not an event_type. We reset the ticket on event change because we don't want the user to be able to select a ticket belonging to the wrong event. We also improve here the comments of the related on-change methods. ---------------------------------------- * website_event_sale: update views to show ticket name and event name The standard behavior was to show the product name in several places instead of the sale order line description. However, for event tickets, we don't want to show the product name, but the ticket name and event name instead. ---------------------------------------- * website_event_sale: add ticket on confirmation page The ticket name was missing from the registration confirmation page (shown when the ticket is free and/or the user paid with a direct method). This is now fixed. ---------------------------------------- Task: 1837987 PR: 25819
This commit is contained in:
@@ -468,7 +468,7 @@
|
||||
<field name="email"/>
|
||||
</group>
|
||||
<group string="Event Information" name="event">
|
||||
<field name="event_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="event_id" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_open': True, 'no_create': True}"/>
|
||||
<field name="date_open" groups="base.group_no_one"/>
|
||||
<field name="date_closed" groups="base.group_no_one"/>
|
||||
</group>
|
||||
|
||||
@@ -160,17 +160,43 @@ class EventTicket(models.Model):
|
||||
def _onchange_product_id(self):
|
||||
self.price = self.product_id.list_price or 0
|
||||
|
||||
def get_ticket_multiline_description_sale(self):
|
||||
""" Compute a multiline description of this ticket, in the context of sales.
|
||||
It will often be used as the default description of a sales order line referencing this ticket.
|
||||
|
||||
1. the first line is the ticket name
|
||||
2. the second line is the event name (if it exists, which should be the case with a normal workflow) or the product name (if it exists)
|
||||
|
||||
We decided to ignore entirely the product name and the product description_sale because they are considered to be replaced by the ticket name and event name.
|
||||
-> the workflow of creating a new event also does not lead to filling them correctly, as the product is created through the event interface
|
||||
"""
|
||||
|
||||
name = self.display_name
|
||||
|
||||
if self.event_id:
|
||||
name += '\n' + self.event_id.display_name
|
||||
elif self.product_id:
|
||||
name += '\n' + self.product_id.display_name
|
||||
|
||||
return name
|
||||
|
||||
|
||||
class EventRegistration(models.Model):
|
||||
_inherit = 'event.registration'
|
||||
|
||||
event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket')
|
||||
event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket', readonly=True, states={'draft': [('readonly', False)]})
|
||||
# in addition to origin generic fields, add real relational fields to correctly
|
||||
# handle attendees linked to sales orders and their lines
|
||||
# TDE FIXME: maybe add an onchange on sale_order_id + origin
|
||||
sale_order_id = fields.Many2one('sale.order', string='Source Sales Order', ondelete='cascade')
|
||||
sale_order_line_id = fields.Many2one('sale.order.line', string='Sales Order Line', ondelete='cascade')
|
||||
|
||||
@api.onchange('event_id')
|
||||
def _onchange_event_id(self):
|
||||
# We reset the ticket when keeping it would lead to an inconstitent state.
|
||||
if self.event_ticket_id and (not self.event_id or self.event_id != self.event_ticket_id.event_id):
|
||||
self.event_ticket_id = None
|
||||
|
||||
@api.multi
|
||||
@api.constrains('event_ticket_id', 'state')
|
||||
def _check_ticket_seats_limit(self):
|
||||
|
||||
@@ -27,14 +27,6 @@ class SaleOrderLine(models.Model):
|
||||
"an event ticket and it will automatically create a registration for this event ticket.")
|
||||
event_ok = fields.Boolean(related='product_id.event_ok', readonly=True)
|
||||
|
||||
@api.multi
|
||||
def _prepare_invoice_line(self, qty):
|
||||
self.ensure_one()
|
||||
res = super(SaleOrderLine, self)._prepare_invoice_line(qty)
|
||||
if self.event_id:
|
||||
res['name'] = '%s: %s' % (res.get('name', ''), self.event_id.name)
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _update_registrations(self, confirm=True, cancel_to_draft=False, registration_data=None):
|
||||
""" Create or update registrations linked to a sales order line. A sale
|
||||
@@ -60,9 +52,40 @@ class SaleOrderLine(models.Model):
|
||||
Registration._prepare_attendee_values(registration))
|
||||
return True
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_id(self):
|
||||
# We reset the event when keeping it would lead to an inconstitent state.
|
||||
# We need to do it this way because the only relation between the product and the event is through the corresponding tickets.
|
||||
if self.event_id and (not self.product_id or self.product_id.id not in self.event_id.mapped('event_ticket_ids.product_id.id')):
|
||||
self.event_id = None
|
||||
|
||||
@api.onchange('event_id')
|
||||
def _onchange_event_id(self):
|
||||
# We reset the ticket when keeping it would lead to an inconstitent state.
|
||||
if self.event_ticket_id and (not self.event_id or self.event_id != self.event_ticket_id.event_id):
|
||||
self.event_ticket_id = None
|
||||
|
||||
@api.onchange('event_ticket_id')
|
||||
def _onchange_event_ticket_id(self):
|
||||
company = self.event_id.company_id or self.env.user.company_id
|
||||
currency = company.currency_id
|
||||
self.price_unit = currency._convert(
|
||||
self.event_ticket_id.price, self.order_id.currency_id, self.order_id.company_id, self.order_id.date_order or fields.Date.today())
|
||||
|
||||
# we call this to force update the default name
|
||||
self.product_id_change()
|
||||
|
||||
def get_sale_order_line_multiline_description_sale(self, product):
|
||||
""" We override this method because we decided that:
|
||||
The default description of a sales order line containing a ticket must be different than the default description when no ticket is present.
|
||||
So in that case we use the description computed from the ticket, instead of the description computed from the product.
|
||||
We need this override to be defined here in sales order line (and not in product) because here is the only place where the event_ticket_id is referenced.
|
||||
"""
|
||||
if self.event_ticket_id:
|
||||
ticket = self.event_ticket_id.with_context(
|
||||
lang=self.order_id.partner_id.lang,
|
||||
)
|
||||
|
||||
return ticket.get_ticket_multiline_description_sale()
|
||||
else:
|
||||
return super(SaleOrderLine, self).get_sale_order_line_multiline_description_sale(product)
|
||||
|
||||
@@ -25,6 +25,12 @@ class EventSaleTest(common.TransactionCase):
|
||||
'date_begin': '2012-01-01 18:05:15'
|
||||
})
|
||||
|
||||
ticket = self.env['event.event.ticket'].create({
|
||||
'name': 'test_ticket',
|
||||
'product_id': product.id,
|
||||
'event_id': event.id,
|
||||
})
|
||||
|
||||
# I create a sales order
|
||||
self.sale_order = self.env['sale.order'].create({
|
||||
'partner_id': self.env.ref('base.res_partner_2').id,
|
||||
@@ -40,7 +46,8 @@ class EventSaleTest(common.TransactionCase):
|
||||
'product_uom_qty': 8.0,
|
||||
'order_id': self.sale_order.id,
|
||||
'name': 'sales order line',
|
||||
'event_id': event.id
|
||||
'event_id': event.id,
|
||||
'event_ticket_id': ticket.id,
|
||||
})
|
||||
|
||||
# In the event registration I add some attendee detail lines. i choose event product
|
||||
|
||||
@@ -29,7 +29,15 @@
|
||||
<field name="inherit_id" ref="event.view_event_registration_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field name="event_ticket_id" domain="[('event_id', '=', event_id)]"/>
|
||||
<field
|
||||
name="event_ticket_id"
|
||||
domain="[
|
||||
('event_id', '=', event_id),
|
||||
'|', ('seats_availability', '=', 'unlimited'), ('seats_available', '>', 0)
|
||||
]"
|
||||
attrs="{'invisible': [('event_id', '=', False)]}"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
/>
|
||||
</field>
|
||||
<group name="event" position="after">
|
||||
<group string="Origin">
|
||||
|
||||
@@ -6,8 +6,28 @@
|
||||
<field name="inherit_id" ref="sale.view_order_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='order_line']//form//field[@name='product_id']" position="after">
|
||||
<field name="event_id" domain="[('event_ticket_ids.product_id','=', product_id),('date_end','>=',time.strftime('%Y-%m-%d 00:00:00'))]" attrs="{'invisible': [('event_ok', '=', False)],'required': [('event_ok', '!=', False)]}"/>
|
||||
<field name="event_ticket_id" domain="[('event_id', '=', event_id), ('product_id','=',product_id), '|', ('seats_availability', '=', 'unlimited'), ('seats_available', '>', 0)]" attrs="{'invisible': [('event_ok', '=', False)], 'required': [('event_ok', '!=', False)]}"/>
|
||||
<field
|
||||
name="event_id"
|
||||
domain="[
|
||||
('event_ticket_ids.product_id','=', product_id),
|
||||
('date_end','>=',time.strftime('%Y-%m-%d 00:00:00'))
|
||||
]"
|
||||
attrs="{'invisible': [('event_ok', '=', False)], 'required': [('event_ok', '!=', False)]}"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
/>
|
||||
<field
|
||||
name="event_ticket_id"
|
||||
domain="[
|
||||
('event_id', '=', event_id),
|
||||
('product_id','=',product_id),
|
||||
'|', ('seats_availability', '=', 'unlimited'), ('seats_available', '>', 0)
|
||||
]"
|
||||
attrs="{
|
||||
'invisible': ['|', ('event_ok', '=', False), ('event_id', '=', False)],
|
||||
'required': [('event_ok', '!=', False), ('event_id', '!=', False)],
|
||||
}"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
/>
|
||||
<field name="event_ok" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']//tree//field[@name='product_id']" position="after">
|
||||
|
||||
@@ -572,6 +572,16 @@ class ProductProduct(models.Model):
|
||||
)
|
||||
return super(ProductProduct, self).get_empty_list_help(help)
|
||||
|
||||
def get_product_multiline_description_sale(self):
|
||||
""" Compute a multiline description of this product, in the context of sales
|
||||
(do not use for purchases or other display reasons that don't intend to use "description_sale").
|
||||
It will often be used as the default description of a sale order line referencing this product.
|
||||
"""
|
||||
name = self.display_name
|
||||
if self.description_sale:
|
||||
name += '\n' + self.description_sale
|
||||
return name
|
||||
|
||||
|
||||
class ProductPackaging(models.Model):
|
||||
_name = "product.packaging"
|
||||
|
||||
@@ -1343,10 +1343,7 @@ class SaleOrderLine(models.Model):
|
||||
self.product_id = False
|
||||
return result
|
||||
|
||||
name = product.name_get()[0][1]
|
||||
if product.description_sale:
|
||||
name += '\n' + product.description_sale
|
||||
vals['name'] = name
|
||||
vals.update(name=self.get_sale_order_line_multiline_description_sale(product))
|
||||
|
||||
self._compute_tax_id()
|
||||
|
||||
@@ -1486,3 +1483,12 @@ class SaleOrderLine(models.Model):
|
||||
def _is_delivery(self):
|
||||
self.ensure_one()
|
||||
return False
|
||||
|
||||
def get_sale_order_line_multiline_description_sale(self, product):
|
||||
""" Compute a default multiline description for this sales order line.
|
||||
This method exists so it can be overridden in other modules to change how the default name is computed.
|
||||
In general only the product is used to compute the name, and this method would not be necessary (we could directly override the method in product).
|
||||
BUT in event_sale we need to know specifically the sales order line as well as the product to generate the name:
|
||||
the product is not sufficient because we also need to know the event_id and the event_ticket_id (both which belong to the sale order line).
|
||||
"""
|
||||
return product.get_product_multiline_description_sale()
|
||||
|
||||
@@ -238,9 +238,7 @@ class SaleOrderOption(models.Model):
|
||||
return
|
||||
product = self.product_id.with_context(lang=self.order_id.partner_id.lang)
|
||||
self.price_unit = product.list_price
|
||||
self.name = product.name
|
||||
if product.description_sale:
|
||||
self.name += '\n' + product.description_sale
|
||||
self.name = product.get_product_multiline_description_sale()
|
||||
self.uom_id = self.uom_id or product.uom_id
|
||||
pricelist = self.order_id.pricelist_id
|
||||
if pricelist and product:
|
||||
|
||||
@@ -4,12 +4,9 @@ import logging
|
||||
import pytz
|
||||
import werkzeug
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -162,8 +159,8 @@ class Event(models.Model):
|
||||
return result
|
||||
|
||||
def _get_event_resource_urls(self, attendees):
|
||||
url_date_start = datetime.strptime(self.date_begin, DEFAULT_SERVER_DATETIME_FORMAT).strftime('%Y%m%dT%H%M%SZ')
|
||||
url_date_stop = datetime.strptime(self.date_end, DEFAULT_SERVER_DATETIME_FORMAT).strftime('%Y%m%dT%H%M%SZ')
|
||||
url_date_start = self.date_begin.strftime('%Y%m%dT%H%M%SZ')
|
||||
url_date_stop = self.date_end.strftime('%Y%m%dT%H%M%SZ')
|
||||
params = {
|
||||
'action': 'TEMPLATE',
|
||||
'text': self.name,
|
||||
|
||||
@@ -42,7 +42,7 @@ class SaleOrder(models.Model):
|
||||
values['event_id'] = ticket.event_id.id
|
||||
values['event_ticket_id'] = ticket.id
|
||||
values['price_unit'] = ticket.price_reduce or ticket.price
|
||||
values['name'] = "%s\n%s" % (ticket.event_id.display_name, ticket.name)
|
||||
values['name'] = ticket.get_ticket_multiline_description_sale()
|
||||
|
||||
# avoid writing related values that end up locking the product record
|
||||
values.pop('event_ok', None)
|
||||
@@ -96,3 +96,18 @@ class SaleOrder(models.Model):
|
||||
# add in return values the registrations, to display them on website (or not)
|
||||
values['attendee_ids'] = self.env['event.registration'].search([('sale_order_line_id', '=', line.id), ('state', '!=', 'cancel')]).ids
|
||||
return values
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
@api.multi
|
||||
@api.depends('product_id.display_name', 'event_ticket_id.display_name')
|
||||
def _compute_name_short(self):
|
||||
""" If the sale order line concerns a ticket, we don't want the product name, but the ticket name instead.
|
||||
"""
|
||||
super(SaleOrderLine, self)._compute_name_short()
|
||||
|
||||
for record in self:
|
||||
if record.event_ticket_id:
|
||||
record.name_short = record.event_ticket_id.display_name
|
||||
|
||||
@@ -84,15 +84,54 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="cart" inherit_id="website_sale.cart_lines" name="Hide product reduction for event tickets">
|
||||
<xpath expr="//td[hasclass('td-product_name')]/div/a" position="attributes">
|
||||
<!-- If the sale order line concerns an event, we want the "product" link to point to the event itself and not to the product on the ecommerce -->
|
||||
<template id="cart_line_product_link_inherit_website_event_sale" inherit_id="website_sale.cart_line_product_link" name="Event Shopping Cart Line Product Link">
|
||||
<xpath expr="//a" position="attributes">
|
||||
<attribute name="t-attf-href"/>
|
||||
<attribute name="t-att-href">
|
||||
line.event_id and ('/event/%s/register' % slug(line.event_id)) or ('/shop/product/%s' % slug(line.product_id.product_tmpl_id))
|
||||
</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name even on small screens -->
|
||||
<template id="cart_lines_inherit_website_event_sale" inherit_id="website_sale.cart_lines" name="Event Shopping Cart Lines">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_line_description_following_lines']/t[@t-set='div_class']" position="after">
|
||||
<t t-if="line.event_id">
|
||||
<t t-set="div_class" t-value="''"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name -->
|
||||
<template id="cart_popover_inherit_website_event_sale" inherit_id="website_sale.cart_popover" name="Event Cart Popover">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_line_product_link']" position="after">
|
||||
<t t-if="line.event_id" t-call="website_sale.cart_line_description_following_lines"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name -->
|
||||
<template id="cart_summary_inherit_website_event_sale" inherit_id="website_sale.cart_summary" name="Event Cart right column">
|
||||
<xpath expr="//td[hasclass('td-product_name')]/div/strong" position="after">
|
||||
<t t-if="line.event_id" t-call="website_sale.cart_line_description_following_lines"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="cart_lines_inherit_website_event_sale" inherit_id="website_sale.cart_lines" name="Hide product reduction for event tickets">
|
||||
<xpath expr="//del" position="attributes">
|
||||
<attribute name="t-attf-class" separator=" " add="#{line.event_id and 'd-none' or ''}"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Add the ticket name on the registration complete page. -->
|
||||
<template id="registration_complete_inherit_website_event_sale" inherit_id="website_event.registration_complete" name="Sale Registration Completed">
|
||||
<xpath expr="//thead/tr/th[1]" position="after">
|
||||
<th>Ticket</th>
|
||||
</xpath>
|
||||
<xpath expr="//tbody/t/tr/td[1]" position="after">
|
||||
<td><i class="fa fa-ticket"></i> <t t-if='attendee.event_ticket_id'><t t-esc="attendee.event_ticket_id.name"/></t><t t-if='not attendee.event_ticket_id'>N/A</t></td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
|
||||
@@ -239,3 +239,21 @@ class SaleOrder(models.Model):
|
||||
self.modified(['create_date'])
|
||||
if self.env.recompute and self.env.context.get('recompute', True):
|
||||
self.recompute()
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
name_short = fields.Char(compute="_compute_name_short")
|
||||
|
||||
@api.multi
|
||||
@api.depends('product_id.display_name')
|
||||
def _compute_name_short(self):
|
||||
""" Compute a short name for this sale order line, to be used on the website where we don't have much space.
|
||||
To keep it short, instead of using the first line of the description, we take the product name without the internal reference.
|
||||
"""
|
||||
for record in self:
|
||||
record.name_short = record.product_id.with_context(display_default_code=False).display_name
|
||||
|
||||
def get_description_following_lines(self):
|
||||
return self.name.splitlines()[1:]
|
||||
|
||||
@@ -817,6 +817,24 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- We use this template where we want to give the user a link to the product of a sale order line. -->
|
||||
<template id="cart_line_product_link" name="Shopping Cart Line Product Link">
|
||||
<a t-attf-href="/shop/product/#{ slug(line.product_id.product_tmpl_id) }">
|
||||
<t t-raw="0"/>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<!-- This template displays all the lines following the first one on the description of the sale order line, with a muted style. For typical products this content will be the product description_sale. -->
|
||||
<template id="cart_line_description_following_lines" name="Shopping Cart Line Description Following Lines">
|
||||
<div t-if="line.get_description_following_lines()" t-attf-class="text-muted {{div_class}} small">
|
||||
<t t-foreach="line.get_description_following_lines()" t-as="name_line">
|
||||
<span><t t-esc="name_line"/></span>
|
||||
<br t-if="not name_line_last" />
|
||||
</t>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- This template is the one at the "Review order" step (the first one) on the checkout workflow. -->
|
||||
<template id="cart_lines" name="Shopping Cart Lines">
|
||||
<div t-if="not website_sale_order or not website_sale_order.website_order_line" class="js_cart_lines alert alert-info">
|
||||
Your cart is empty!
|
||||
@@ -840,15 +858,13 @@
|
||||
</td>
|
||||
<td t-if="line.product_id.product_tmpl_id" class='td-product_name'>
|
||||
<div>
|
||||
<a t-attf-href="/shop/product/#{ slug(line.product_id.product_tmpl_id) }">
|
||||
<strong t-esc="line.product_id.with_context(display_default_code=False).display_name" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-muted d-none d-md-block small">
|
||||
<t t-foreach="line.name.splitlines()[1:]" t-as="name_line">
|
||||
<span><t t-esc="name_line"/></span><br/>
|
||||
<t t-call="website_sale.cart_line_product_link">
|
||||
<strong t-field="line.name_short" />
|
||||
</t>
|
||||
</div>
|
||||
<t t-call="website_sale.cart_line_description_following_lines">
|
||||
<t t-set="div_class" t-value="'d-none d-md-block'"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-center td-qty">
|
||||
<div class="css_quantity input-group oe_website_spinner">
|
||||
@@ -943,6 +959,7 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- this template is the one when we mouse over "My Cart" on the top right -->
|
||||
<template id="cart_popover" name="Cart Popover">
|
||||
<div t-if="not website_sale_order or not website_sale_order.website_order_line" class="alert alert-info">
|
||||
Your cart is empty!
|
||||
@@ -954,10 +971,11 @@
|
||||
<span t-field="line.product_id.image_small" t-options="{'widget': 'image', 'class': 'rounded'}" />
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<a t-attf-href="/shop/product/#{ slug(line.product_id.product_tmpl_id) }">
|
||||
<span t-esc="line.product_id.with_context(display_default_code=False).display_name" class="h6" />
|
||||
</a>
|
||||
<br />
|
||||
<div>
|
||||
<t t-call="website_sale.cart_line_product_link">
|
||||
<span class="h6" t-esc="line.name_short" />
|
||||
</t>
|
||||
</div>
|
||||
<small>Qty: <t t-esc="int(line.product_uom_qty) == line.product_uom_qty and int(line.product_uom_qty) or line.product_uom_qty" /></small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1388,6 +1406,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- This template is the one present on the right during the payment process.
|
||||
Here it is important to not show too much information to the user, because we want him to pay!
|
||||
We shouldn't display link to products or long descriptions.
|
||||
-->
|
||||
<template id="cart_summary" name="Cart right column">
|
||||
<div class="card">
|
||||
<div class="card-body p-xl-0">
|
||||
@@ -1416,9 +1438,9 @@
|
||||
</td>
|
||||
<td class='td-product_name' t-if="line.product_id.product_tmpl_id">
|
||||
<div>
|
||||
<strong t-field="line.product_id.with_context(display_default_code=False).display_name" />
|
||||
<strong t-field="line.name_short" />
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
<td class='td-qty'>
|
||||
<div t-esc="line.product_uom_qty" />
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user