[IMP] website_{portal,portal_sale,project_issue,quote} : add pager for customer documents

This commit adds controller to list documents of customer, with pager and archive
widget to sort documents. Impacted documents are : quotations, sale order,
invoices, and issues.

Portal breadcrumd is improved too : add the home icon, and keep the query
params when using archive widget.

Some editable zones were added to templates, allowing people to customize
their frontend portal.
This commit is contained in:
Vipul Bhatt
2016-05-27 10:50:34 +02:00
committed by Jérome Maes
parent a999969f81
commit 4dfbcecfdb
10 changed files with 395 additions and 84 deletions
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="header_footer_custom_payment" inherit_id="website_portal.account" customize_show="True" active="False" name="Payment Methods">
<template id="header_footer_custom_payment" inherit_id="website_portal.portal_layout" customize_show="True" active="False" name="Payment Methods">
<xpath expr="//div[@class='o_my_details']" position="inside">
<h4 class="payment_method_title">Payment Method</h4>
<ul class="payment_method_list list-inline">
@@ -30,7 +30,7 @@
<div class="row">
<div class="col-sm-6">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li><a href="/my/home"><i class="fa fa-home"/></a></li>
<li>Payment Methods</li>
</ol>
</div>
@@ -78,7 +78,7 @@
</t>
</template>
<template id="pay_meth_link" inherit_id="website_portal.account">
<template id="pay_meth_link" inherit_id="website_portal.portal_layout">
<xpath expr="//div[@class='o_my_details']" position="inside">
<a href="/my/payment_method">Manage your payment methods</a>
</xpath>
+31 -4
View File
@@ -4,12 +4,16 @@ from openerp.http import request
from openerp import tools
from openerp.tools.translate import _
from odoo.fields import Date
class website_account(http.Controller):
@http.route(['/my', '/my/home'], type='http', auth="public", website=True)
def account(self):
partner = request.env.user.partner_id
_items_per_page = 10
def _prepare_portal_layout_values(self):
""" prepare the values to render portal layout """
partner = request.env.user.partner_id
# get customer sales rep
if partner.user_id:
sales_rep = partner.user_id
@@ -20,8 +24,31 @@ class website_account(http.Controller):
'company': request.website.company_id,
'user': request.env.user
}
return values
return request.website.render("website_portal.account", values)
def _get_archive_groups(self, model, domain=None, fields=None, groupby="create_date", order="create_date desc"):
if not model:
return []
if domain is None:
domain = []
if fields is None:
fields = ['name', 'create_date']
groups = []
for group in request.env[model]._read_group_raw(domain, fields=fields, groupby=groupby, orderby=order):
dates, label = group[groupby]
date_begin, date_end = dates.split('/')
groups.append({
'date_begin': Date.to_string(Date.from_string(date_begin)),
'date_end': Date.to_string(Date.from_string(date_end)),
'name': label,
'item_count': group[groupby + '_count']
})
return groups
@http.route(['/my', '/my/home'], type='http', auth="public", website=True)
def account(self):
values = self._prepare_portal_layout_values()
return request.website.render("website_portal.portal_my_home", values)
@http.route(['/my/account'], type='http', auth='user', website=True)
def details(self, redirect=None, **post):
@@ -1,9 +1,10 @@
odoo.define('website_portal', function(require) {
'use strict';
require('website.website');
if(!$('.o_website_portal_details, .o_my_show_more').length) {
return $.Deferred().reject("DOM doesn't contain '.o_website_portal_details' or '.o_my_show_more'");
if(!$('.o_website_portal_details').length) {
return $.Deferred().reject("DOM doesn't contain '.o_website_portal_details'");
}
var state_options = $("select[name='state_id']:enabled option:not(:first)");
@@ -15,10 +16,4 @@ odoo.define('website_portal', function(require) {
select.parent().toggle(nb>=1);
});
$('.o_website_portal_details').find("select[name='country_id']").change();
$('.o_my_show_more').on('click', function(ev) {
ev.preventDefault();
$(this).parents('table').find(".to_hide").toggleClass('hidden');
$(this).find('span').toggleClass('hidden');
});
});
@@ -15,4 +15,8 @@
text-align: left;
}
table-layout: fixed;
}
}
.o_my_home_content > .page-header > a:hover {
text-decoration: none;
}
+56 -10
View File
@@ -8,30 +8,76 @@
</xpath>
</template>
<template id="account" name="Account">
<template id="portal_archive_groups" name="Portal Archive Groups">
<h3 class="page-header">Archives</h3>
<ul class="nav nav-pills nav-stacked">
<t t-foreach="archive_groups" t-as="group">
<t t-if="group['date_begin'] == date">
<li class="active">
<a t-att-href="default_url" t-ignore="True"><t t-esc="group['name']"/><span class="pull-right badge" t-esc="group['item_count']"/></a>
</li>
</t>
<t t-if="group['date_begin'] != date">
<li>
<a t-ignore="True" t-attf-href="#{default_url}?date_begin=#{group['date_begin']}&amp;date_end=#{group['date_end']}"><t t-esc="group['name']"/><span class="pull-right badge" t-esc="group['item_count']"/></a>
</li>
</t>
</t>
</ul>
</template>
<template id="portal_layout" name="Portal Layout">
<t t-call="website.layout">
<div class="container mt16">
<div class="navbar navbar-default">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#oe-help-navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/my/home">
My Account
</a>
</div>
<div class="collapse navbar-collapse" id="oe-help-navbar-collapse">
<ul class="nav navbar-nav o_portal_submenu">
</ul>
</div>
</div>
</div>
<div id="wrap">
<div class="container mb64">
<div class="col-md-8 o_my_documents">
<div class="oe_structure"/>
</div>
<div t-if="sales_rep" class="col-md-3">
<t t-call="website_portal.contact" />
<div class="oe_structure"/>
<div class="col-md-8">
<t t-raw="0"/>
</div>
<div id="o_my_sidebar" class="col-md-3 col-md-offset-1 o_my_sidebar">
<div class="o_my_contact" t-if="sales_rep">
<t t-call="website_portal.contact"/>
</div>
<div class="o_my_details">
<h3 class="page-header">Your Details <a href="/my/account" class="btn btn-default btn-xs">Change</a></h3>
<div t-field="user.partner_id" t-field-options='{"widget": "contact", "fields": ["email", "phone", "address", "name"]}'/>
</div>
<div class="o_my_archives" t-if="archive_groups">
<t t-call="website_portal.portal_archive_groups"/>
</div>
</div>
<div class="col-md-12 oe_structure"/>
</div>
</div>
</t>
</template>
<template id="website_portal.custom_panel" inherit_id='website_portal.account' customize_show="True" name="Editable Panel" active="False">
<template id="portal_my_home" name="My Portal">
<t t-call="website_portal.portal_layout">
<div class="o_my_home_content">
<div class="oe_structure"></div>
</div>
<div class="col-md-12 oe_structure"/>
</t>
</template>
<template id="website_portal.custom_panel" inherit_id='website_portal.portal_layout' customize_show="True" name="Editable Panel" active="False">
<xpath expr="//div[@id='o_my_sidebar']" position="inside">
<div class="oe_structure">
<h3 class="page-header">Custom Panel</h3>
@@ -56,7 +102,7 @@
<div class="row">
<div class="col-sm-6">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li><a href="/my/home"><i class="fa fa-home"/></a></li>
<li>Details</li>
</ol>
</div>
+131 -13
View File
@@ -9,45 +9,163 @@ from openerp.addons.website_portal.controllers.main import website_account
class website_account(website_account):
@http.route(['/my/home'], type='http', auth="user", website=True)
def account(self, **kw):
@http.route()
def account(self):
""" Add sales documents to main account page """
response = super(website_account, self).account()
partner = request.env.user.partner_id
res_sale_order = request.env['sale.order']
res_invoices = request.env['account.invoice']
quotations = res_sale_order.search([
SaleOrder = request.env['sale.order']
Invoice = request.env['account.invoice']
quotation_count = SaleOrder.search_count([
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('state', 'in', ['sent', 'cancel'])
])
orders = res_sale_order.search([
order_count = SaleOrder.search_count([
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('state', 'in', ['sale', 'done'])
])
invoices = res_invoices.search([
invoice_count = Invoice.search_count([
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('state', 'in', ['open', 'paid', 'cancelled'])
])
response.qcontext.update({
'date': datetime.date.today().strftime('%Y-%m-%d'),
'quotations': quotations,
'orders': orders,
'invoices': invoices,
'quotation_count': quotation_count,
'order_count': order_count,
'invoice_count': invoice_count,
})
return response
#
# Quotations and Sale Orders
#
@http.route(['/my/quotes', '/my/quotes/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_quotes(self, page=1, date_begin=None, date_end=None, **kw):
values = self._prepare_portal_layout_values()
partner = request.env.user.partner_id
SaleOrder = request.env['sale.order']
domain = [
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('state', 'in', ['sent', 'cancel'])
]
archive_groups = self._get_archive_groups('sale.order', domain)
if date_begin and date_end:
domain += [('create_date', '>=', date_begin), ('create_date', '<', date_end)]
# count for pager
quotation_count = SaleOrder.search_count(domain)
# make pager
pager = request.website.pager(
url="/my/quotes",
url_args={'date_begin': date_begin, 'date_end': date_end},
total=quotation_count,
page=page,
step=self._items_per_page
)
# search the count to display, according to the pager data
quotations = SaleOrder.search(domain, limit=self._items_per_page, offset=pager['offset'])
values.update({
'date': date_begin,
'quotations': quotations,
'pager': pager,
'archive_groups': archive_groups,
'default_url': '/my/quotes',
})
return request.website.render("website_portal_sale.portal_my_quotations", values)
@http.route(['/my/orders', '/my/orders/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_orders(self, page=1, date_begin=None, date_end=None, **kw):
values = self._prepare_portal_layout_values()
partner = request.env.user.partner_id
SaleOrder = request.env['sale.order']
domain = [
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('state', 'in', ['sale', 'done'])
]
archive_groups = self._get_archive_groups('sale.order', domain)
if date_begin and date_end:
domain += [('create_date', '>=', date_begin), ('create_date', '<', date_end)]
# count for pager
order_count = SaleOrder.search_count(domain)
# pager
pager = request.website.pager(
url="/my/orders",
url_args={'date_begin': date_begin, 'date_end': date_end},
total=order_count,
page=page,
step=self._items_per_page
)
# content according to pager and archive selected
orders = SaleOrder.search(domain, limit=self._items_per_page, offset=pager['offset'])
values.update({
'date': date_begin,
'orders': orders,
'page_name': 'order',
'pager': pager,
'archive_groups': archive_groups,
'default_url': '/my/orders',
})
return request.website.render("website_portal_sale.portal_my_orders", values)
@http.route(['/my/orders/<int:order>'], type='http', auth="user", website=True)
def orders_followup(self, order=None):
def orders_followup(self, order=None, **kw):
order = request.env['sale.order'].browse([order])
try:
order.check_access_rights('read')
order.check_access_rule('read')
except AccessError:
return request.website.render("website.403")
return request.website.render("website.403")
order_invoice_lines = {il.product_id.id: il.invoice_id for il in order.invoice_ids.mapped('invoice_line_ids')}
return request.website.render("website_portal_sale.orders_followup", {
'order': order.sudo(),
'order_invoice_lines': order_invoice_lines,
})
#
# Invoices
#
@http.route(['/my/invoices', '/my/invoices/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_invoices(self, page=1, date_begin=None, date_end=None, **kw):
values = self._prepare_portal_layout_values()
partner = request.env.user.partner_id
AccountInvoice = request.env['account.invoice']
domain = [
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
('state', 'in', ['open', 'paid', 'cancelled'])
]
archive_groups = self._get_archive_groups('account.invoice', domain)
if date_begin and date_end:
domain += [('create_date', '>=', date_begin), ('create_date', '<', date_end)]
# count for pager
invoice_count = AccountInvoice.search_count(domain)
# pager
pager = request.website.pager(
url="/my/invoices",
url_args={'date_begin': date_begin, 'date_end': date_end},
total=invoice_count,
page=page,
step=self._items_per_page
)
# content according to pager and archive selected
invoices = AccountInvoice.search(domain, limit=self._items_per_page, offset=pager['offset'])
values.update({
'date': date_begin,
'invoices': invoices,
'page_name': 'invoice',
'pager': pager,
'archive_groups': archive_groups,
'default_url': '/my/invoices',
})
return request.website.render("website_portal_sale.portal_my_invoices", values)
+87 -24
View File
@@ -6,15 +6,63 @@
</xpath>
</template>
<template id="account" name="Account" inherit_id="website_portal.account">
<xpath expr="//div[contains(@class,'o_my_documents')]" position="inside">
<t t-call="website_portal_sale.quotations" />
<t t-call="website_portal_sale.sale_orders" />
<t t-call="website_portal_sale.invoices" />
<template id="portal_my_home_menu_sale" name="Portal layout : sales menu entries" inherit_id="website_portal.portal_layout" priority="25">
<xpath expr="//ul[contains(@class,'o_portal_submenu')]" position="inside">
<li>
<a href="/my/quotes">Quotes</a>
</li>
<li>
<a href="/my/orders">Orders</a>
</li>
<li>
<a href="/my/invoices">Invoices</a>
</li>
</xpath>
</template>
<template id="quotations" name="Quotations">
<template id="portal_my_home_sale" name="Portal My Home : sales entries" inherit_id="website_portal.portal_my_home" priority="25">
<xpath expr="//div[contains(@class,'o_my_home_content')]" position="inside">
<h3 class="page-header">
<a href="/my/quotes">Your Quotes
<small class="ml8">
<t t-if="quotation_count">
<span class='badge'><t t-esc="quotation_count"/></span>
</t>
<t t-if="not quotation_count">
There are currently no quotes for your account.
</t>
</small>
</a>
</h3>
<h3 class="page-header">
<a href="/my/orders">Your Orders
<small class="ml8">
<t t-if="order_count">
<span class='badge'><t t-esc="order_count"/></span>
</t>
<t t-if="not order_count">
There are currently no orders for your account.
</t>
</small>
</a>
</h3>
<h3 class="page-header">
<a href="/my/invoices">Your Invoices and Payments
<small class="ml8">
<t t-if="invoice_count">
<span class='badge'><t t-esc="invoice_count"/></span>
</t>
<t t-if="not invoice_count">
There are currently no invoices for your account.
</t>
</small>
</a>
</h3>
</xpath>
</template>
<template id="portal_my_quotations" name="My Quotations">
<t t-call="website_portal.portal_layout">
<h3 class="page-header">Your Quotes</h3>
<t t-if="not quotations">
<p>There are currently no quotes for your account.</p>
@@ -30,9 +78,9 @@
</tr>
</thead>
<t t-foreach="quotations" t-as="quotation">
<tr t-att-class="'hidden to_hide' if quotation.state!='sent' and quotation_index &gt; 4 else ''">
<tr>
<td>
<a t-att-href="'/my/orders/'+str(quotation.id)"><t t-esc="quotation.name"/></a>
<a t-attf-href="/my/orders/#{quotation.id}?#{keep_query()}"><t t-esc="quotation.name"/></a>
</td>
<td><span t-field="quotation.validity_date"/></td>
<td>
@@ -47,14 +95,16 @@
</td>
</tr>
</t>
<tr t-att-class="'hidden' if len(quotations) &lt; 5 else ''">
<td colspan="5"><a href="" class="o_my_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a></td>
</tr>
</table>
<div t-if="pager" class="o_portal_pager text-center">
<t t-call="website.pager"/>
</div>
</t>
</t>
</template>
<template id="sale_orders" name="Sales Orders">
<template id="portal_my_orders" name="My Sales Orders">
<t t-call="website_portal.portal_layout">
<h3 class="page-header">Your Orders</h3>
<t t-if="not orders">
<p>There are currently no orders for your account.</p>
@@ -70,9 +120,9 @@
</tr>
</thead>
<t t-foreach="orders" t-as="order">
<tr t-att-class="'hidden to_hide' if order.state not in ['manual', 'progress'] and order_index &gt; 4 else ''">
<tr>
<td>
<a t-att-href="'/my/orders/'+str(order.id)"><t t-esc="order.name"/></a>
<a t-attf-href="/my/orders/{{order.id}}?{{keep_query()}}"><t t-esc="order.name"/></a>
</td>
<td><span t-field="order.date_order"/></td>
<td>
@@ -89,14 +139,16 @@
<td><span t-field="order.amount_total" t-field-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/></td>
</tr>
</t>
<tr t-att-class="'hidden' if len(orders) &lt; 5 else ''">
<td colspan="4"><a href="" class="o_my_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a></td>
</tr>
</table>
<div t-if="pager" class="o_portal_pager text-center">
<t t-call="website.pager"/>
</div>
</t>
</t>
</template>
<template id="invoices" name="Invoices and Payments">
<template id="portal_my_invoices" name="My Invoices and Payments">
<t t-call="website_portal.portal_layout">
<h3 class="page-header">Your Invoices and Payments</h3>
<t t-if="not invoices">
<p>There are currently no invoices for your account.</p>
@@ -114,7 +166,7 @@
</tr>
</thead>
<t t-foreach="invoices" t-as="invoice">
<tr t-att-class="'hidden to_hide' if invoice.state!='open' and invoice_index &gt; 4 else ''">
<tr>
<td>
<a t-att-href="'/report/pdf/account.report_invoice/'+str(invoice.id)"><t t-esc="invoice.number"/></a>
</td>
@@ -137,11 +189,12 @@
<td><span t-field="invoice.residual" t-field-options='{"widget": "monetary", "display_currency": "invoice.currency_id"}'/></td>
</tr>
</t>
<tr t-att-class="'hidden' if len(invoices) &lt; 5 else ''">
<td colspan="5"><a href="" class="o_my_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a></td>
</tr>
</table>
<div t-if="pager" class="o_portal_pager text-center">
<t t-call="website.pager"/>
</div>
</t>
</t>
</template>
<template id="orders_followup" name="Sales Order">
@@ -152,7 +205,17 @@
<div class="row">
<div class="col-sm-4">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li><a href="/my/home"><i class="fa fa-home"/></a></li>
<li>
<a t-attf-href="/my/#{'quotes' if order.state == 'sent' else 'orders'}?{{ keep_query()}}">
<t t-if="order.state == 'sent'">
My Quotes
</t>
<t t-if="order.state != 'sent'">
My Orders
</t>
</a>
</li>
<li>
<t t-if="order.state == 'sent'">
Quotation
@@ -318,7 +381,7 @@
</t>
</template>
<template id="sale_backend_link" inherit_id='website_portal_sale.sale_orders' customize_show="True" name="Link to Sales Backend" active="False">
<template id="sale_backend_link" inherit_id='website_portal_sale.portal_my_orders' customize_show="True" name="Link to Sales Backend" active="False">
<xpath expr="//h3[@class='page-header']" position="inside">
<a href="/web#action=portal_sale.action_orders_portal" target="_blank" class="btn btn-default btn-xs pull-right"> Access the complete Sale application</a>
</xpath>
@@ -6,17 +6,46 @@ from openerp.http import request
class WebsiteAccount(website_account):
@http.route(['/my', '/my/home'], type='http', auth="user", website=True)
@http.route()
def account(self):
response = super(WebsiteAccount, self).account()
user = request.env.user
project_issues = request.env['project.issue'].search([])
response.qcontext.update({'issues': project_issues})
issue_count = request.env['project.issue'].search_count([])
response.qcontext.update({'issue_count': issue_count})
return response
@http.route(['/my/issues', '/my/issues/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_issues(self, page=1, date_begin=None, date_end=None, **kw):
values = self._prepare_portal_layout_values()
ProjectIssue = request.env['project.issue']
domain = []
# archive groups - Default Group By 'create_date'
archive_groups = self._get_archive_groups('project.issue', domain)
if date_begin and date_end:
domain += [('create_date', '>=', date_begin), ('create_date', '<', date_end)]
# pager
issue_count = ProjectIssue.search_count(domain)
pager = request.website.pager(
url="/my/issues",
url_args={'date_begin': date_begin, 'date_end': date_end},
total=issue_count,
page=page,
step=self._items_per_page
)
# content according to pager and archive selected
project_issues = ProjectIssue.search(domain, order="stage_id", limit=self._items_per_page, offset=pager['offset'])
values.update({
'date': date_begin,
'issues': project_issues,
'page_name': 'issue',
'archive_groups': archive_groups,
'default_url': '/my/issues',
'pager': pager
})
return request.website.render("website_project_issue.portal_my_issues", values)
class WebsiteProjectIssue(http.Controller):
@http.route(['/my/issues/<int:issue_id>'], type='http', auth="user", website=True)
def issues_followup(self, issue_id=None):
def issues_followup(self, issue_id=None, **kw):
issue = request.env['project.issue'].browse(issue_id)
return request.website.render("website_project_issue.issues_followup", {'issue': issue})
@@ -1,7 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_project_issue" name="Portal Project Issues" inherit_id="website_portal.account">
<xpath expr="//div[contains(@class,'o_my_documents')]/*[last()]" position="after">
<template id="portal_my_home_menu_issue" name="Portal layout : issues menu entry" inherit_id="website_portal.portal_layout" priority="30">
<xpath expr="//ul[contains(@class,'o_portal_submenu')]" position="inside">
<li t-att-class="page_name == 'issue' and 'active' or ''">
<a href="/my/issues">Issues</a>
</li>
</xpath>
</template>
<template id="portal_my_home_issue" name="Portal My Home : issue entries" inherit_id="website_portal.portal_my_home" priority="30">
<xpath expr="//div[contains(@class,'o_my_home_content')]" position="inside">
<h3 class="page-header">
<a href="/my/issues">Your Issues
<small class="ml8">
<t t-if="issue_count">
<span class='badge'><t t-esc="issue_count"/></span>
</t>
<t t-if="not issue_count">
There are currently no issues for your account.
</t>
</small>
</a>
</h3>
</xpath>
</template>
<template id="portal_my_issues" name="My Issues" priority="30">
<t t-call="website_portal.portal_layout">
<h3 class="page-header">Your Issues</h3>
<t t-if="not issues">
<p>There are currently no issues for your account.</p>
@@ -16,20 +42,21 @@
</tr>
</thead>
<t t-foreach="issues" t-as="issue">
<tr t-att-class="'hidden to_hide' if issue_index &gt; 4 else ''">
<td><a t-attf-href="/my/issues/#{issue.id}">Issue <t t-esc="issue.id"/></a></td>
<tr>
<td>
<a t-attf-href="/my/issues/#{issue.id}?{{ keep_query() }}">Issue <t t-esc="issue.id"/></a>
</td>
<td><span t-field="issue.name"/></td><td/>
<td><span t-field="issue.stage_id.name"/></td>
</tr>
</t>
<tr t-att-class="'hidden' if len(issues) &lt; 6 else ''">
<td colspan="4">
<a href="" class="o_my_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a>
</td>
</tr>
</table>
<div t-if="pager" class="o_portal_pager text-center">
<t t-call="website.pager"/>
</div>
</t>
</xpath>
</t>
</template>
<template id="issues_followup" name="Project Issues">
@@ -40,7 +67,8 @@
<div class="row">
<div class="col-sm-4">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li><a href="/my/home"><i class="fa fa-home"/></a></li>
<li><a t-attf-href="/my/issues?#{keep_query()}">My Issues</a></li>
<li>Issue <span t-field="issue.id"/></li>
</ol>
</div>
@@ -162,7 +162,8 @@
<div t-if="breadcrumb" class="row">
<div class="col-sm-4">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li><a href="/my/home"><i class="fa fa-home"/></a></li>
<li><a t-attf-href="/my/quotes?#{keep_query()}">My Quotations</a></li>
<li>
Quotation <t t-esc="quotation.name"/>
</li>
@@ -682,12 +683,12 @@
</xpath>
</template>
<template id="quotations" inherit_id="website_portal_sale.quotations">
<template id="quotations" inherit_id="website_portal_sale.portal_my_quotations">
<xpath expr="//t[@t-foreach='quotations']" position="replace">
<t t-foreach="quotations" t-as="quotation">
<tr t-att-class="'hidden to_hide' if quotation_index &gt; 4 else ''">
<tr>
<td>
<a t-att-href="'/quote/'+str(quotation.id)"><t t-esc="quotation.name"/></a>
<a t-attf-href="/quote/#{quotation.id}?#{keep_query()}"><t t-esc="quotation.name"/></a>
</td>
<td><span t-field="quotation.validity_date"/></td>
<td>