[FIX] various: clean up portal and reports (sales and invoices)

The goal of this commit is to fix the various views and reports of sales and invoices.

======
Portal
======

Fix breadcrumbs

Fix BS4 issues, alignment of various elements

Better auto resize of the invoice iframe

Fix invoice list for draft invoices

Fix previous/next document pagers

Remove unnecessary monetary widgets (not needed when using t-field if the field is Monetary)

Standardize invoice and sale routes:
- especially when related to displaying: html, pdf (print and download), text
- by using the get_portal_url() also for invoices

Add the total on the left column of sales order

================
Section and note
================

Improve general style:
- notes in italics
- sections: less padding, darker background, no borders

=========
Demo Data
=========

Fix sales order demo data to appear for the admin (instead of system)

PR: #25997
task-1869469
This commit is contained in:
Sébastien Theys
2018-08-29 17:16:06 +02:00
parent 984554f4ca
commit 4a3fd02af4
30 changed files with 419 additions and 438 deletions
+4 -33
View File
@@ -1,9 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from werkzeug.exceptions import NotFound
from odoo import http, _
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.exceptions import AccessError, MissingError
@@ -31,11 +28,9 @@ class PortalAccount(CustomerPortal):
}
return self._get_page_view_values(invoice, access_token, values, 'my_invoices_history', False, **kwargs)
@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, sortby=None, **kw):
values = self._prepare_portal_layout_values()
partner = request.env.user.partner_id
AccountInvoice = request.env['account.invoice']
domain = []
@@ -82,42 +77,18 @@ class PortalAccount(CustomerPortal):
return request.render("account.portal_my_invoices", values)
@http.route(['/my/invoices/<int:invoice_id>'], type='http', auth="public", website=True)
def portal_my_invoice_detail(self, invoice_id, access_token=None, **kw):
def portal_my_invoice_detail(self, invoice_id, access_token=None, report_type=None, download=False, **kw):
try:
invoice_sudo = self._document_check_access('account.invoice', invoice_id, access_token)
except (AccessError, MissingError):
return request.redirect('/my')
if report_type in ('html', 'pdf', 'text'):
return self._show_report(model=invoice_sudo, report_type=report_type, report_ref='account.account_invoices', download=download)
values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw)
return request.render("account.portal_invoice_page", values)
@http.route([
'/my/invoices/pdf/<int:invoice_id>',
'/my/invoices/html/<int:invoice_id>/<string:access_token>'
], type='http', auth="public", website=True)
def portal_my_invoice_report(self, invoice_id, access_token=None, **kw):
try:
invoice_sudo = self._document_check_access('account.invoice', invoice_id, access_token)
except (AccessError, MissingError):
return request.redirect('/my')
# print report as sudo, since it require access to taxes, payment term, ... and portal
# does not have those access rights.
accountInvoiceReport = request.env.ref('account.account_invoices').sudo()
report_type = kw.get('report_type', 'pdf')
method_name = 'render_qweb_%s' % (report_type)
if hasattr(accountInvoiceReport, method_name):
invoice_report = getattr(accountInvoiceReport, method_name)([invoice_sudo.id], data={'report_type': report_type})[0]
reporthttpheaders = [
('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'),
('Content-Length', len(invoice_report)),
]
if report_type == 'pdf' and not kw.get('print'):
filename = "%s.pdf" % (re.sub('\W+', '-', invoice_sudo._get_printed_report_name()))
reporthttpheaders.append(('Content-Disposition', http.content_disposition(filename)))
return request.make_response(invoice_report, headers=reporthttpheaders)
raise NotFound()
# ------------------------------------------------------------
# My Home
# ------------------------------------------------------------
+1 -10
View File
@@ -479,7 +479,7 @@ class AccountInvoice(models.Model):
sequence.number_next = int(result.group(2))
@api.multi
def _get_printed_report_name(self):
def _get_report_base_filename(self):
self.ensure_one()
return self.type == 'out_invoice' and self.state == 'draft' and _('Draft Invoice') or \
self.type == 'out_invoice' and self.state in ('open','in_payment','paid') and _('Invoice - %s') % (self.number) or \
@@ -1512,15 +1512,6 @@ class AccountInvoice(models.Model):
len(res),
) for r in res]
@api.multi
def get_portal_url(self, suffix=None):
"""
Get a portal url for this invoice, including access_token.
- suffix: string to append to the url, before the query string
"""
self.ensure_one()
return self.access_url + '%s?access_token=%s' % (suffix if suffix else '', self._portal_ensure_token())
@api.multi
def preview_invoice(self):
self.ensure_one()
@@ -186,5 +186,5 @@ class ReportInvoiceWithPayment(models.AbstractModel):
'doc_ids': docids,
'doc_model': report.model,
'docs': self.env[report.model].browse(docids),
'type_html': data and data.get('report_type') == 'html',
'report_type': data.get('report_type') if data else '',
}
@@ -32,7 +32,9 @@ var AccountPortalSidebar = PortalSidebar.extend({
var self = this;
this._super.apply(this, arguments);
var $invoiceHtml = this.$el.find('iframe#invoice_html');
$invoiceHtml.on('load', self._onLoadIframe.bind(self, $invoiceHtml));
var updateIframeSize = self._updateIframeSize.bind(self, $invoiceHtml);
$invoiceHtml.on('load', updateIframeSize);
$(window).on('resize', updateIframeSize);
},
//--------------------------------------------------------------------------
@@ -40,17 +42,17 @@ var AccountPortalSidebar = PortalSidebar.extend({
//--------------------------------------------------------------------------
/**
* Called when the iframe is load on custome portal
* here we set height and width of html preview (iframe)
* Called when the iframe is loaded or the window is resized on customer portal.
* The goal is to expand the iframe height to display the full report without scrollbar.
*
* @private
* @param {object} $el: the iframe
*/
_onLoadIframe: function ($el) {
_updateIframeSize: function ($el) {
var $body = $el.contents().find('body');
// expand iframe height for display full report on the custome portal (no scrollbar in iframe)
$el.height($body.scrollParent().height());
// removed extra space in html preview (specially left and right margin)
$body.width('100%');
// Set it to 0 first to handle the case where scrollHeight is too big for its content.
$el.height(0);
$el.height($body[0].scrollHeight);
},
/**
* @private
@@ -3,13 +3,14 @@
// section and note on sale order and invoice.
table.o_section_and_note_list_view tr.o_data_row.o_is_line_note,
table.o_section_and_note_list_view tr.o_data_row.o_is_line_note textarea[name="name"],
div.oe_kanban_card.o_is_line_note {
color: #999;
font-style: italic;
}
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section,
div.oe_kanban_card.o_is_line_section {
font-weight: bold;
background-color: #EEE;
background-color: #DDDDDD;
}
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section {
border-top: 1px solid #BBB;
@@ -490,8 +490,8 @@
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="quantity"/>
<field name="uom_id" groups="uom.group_uom"/>
<field name="price_unit"/>
<field name="discount" groups="base.group_no_one"/>
<field name="price_unit" string="Price"/>
<field name="discount" groups="base.group_no_one" string="Disc (%)"/>
<field name="invoice_line_tax_ids" widget="many2many_tags" options="{'no_create': True}" context="{'type':parent.type, 'tree_view_ref': 'account.account_tax_view_tree', 'search_view_ref': 'account.account_tax_view_search'}"
domain="[('type_tax_use','=','sale'),('company_id', '=', parent.company_id)]"/>
<field name="price_subtotal" string="Subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
@@ -6,7 +6,8 @@
<t t-else="">Invoices</t>
</li>
<li t-if="invoice" class="breadcrumb-item">
<t t-esc="invoice.number"/>
<t t-esc="invoice.number" t-if="invoice.number"/>
<t t-else="">Draft Invoice</t>
</li>
</xpath>
</template>
@@ -42,7 +43,10 @@
<t t-foreach="invoices" t-as="invoice">
<tr>
<td>
<a t-attf-href="/my/invoices/#{invoice.id}" t-att-title="invoice.number"><t t-esc="invoice.number"/></a>
<a t-att-href="invoice.get_portal_url()" t-att-title="invoice.number">
<t t-esc="invoice.number" t-if="invoice.number"/>
<t t-else="">Draft Invoice</t>
</a>
</td>
<td><span t-field="invoice.date_invoice"/></td>
<td class='d-none d-md-table-cell'><span t-field="invoice.date_due"/></td>
@@ -85,34 +89,47 @@
</div>
<div class="o_payment_button">
<div class="text-center mb8 ml16 mr16 o_download_pdf">
<a class="btn btn-secondary btn-block o_download_btn" t-att-href="'/my/invoices/pdf/%s?%s' % (invoice.id, keep_query())" title="Download"><i class="fa fa-download"/> Download</a>
<a class="btn btn-secondary btn-block d-none d-lg-inline-block o_print_btn o_portal_invoice_print" t-att-href="'/my/invoices/pdf/%s?%s&amp;print=true' % (invoice.id, keep_query())" id="print_invoice_report" title="Print"><i class="fa fa-print"/> Print</a>
<a class="btn btn-secondary btn-block o_download_btn" t-att-href="invoice.get_portal_url(report_type='pdf', download=True)" title="Download"><i class="fa fa-download"/> Download</a>
<a class="btn btn-secondary btn-block d-none d-lg-inline-block o_print_btn o_portal_invoice_print" t-att-href="invoice.get_portal_url(report_type='pdf')" id="print_invoice_report" title="Print"><i class="fa fa-print"/> Print</a>
</div>
</div>
<hr t-if="invoice.user_id" class="mt0 mb0"/>
<div class="pb8 ml16 mr16 container">
<t t-if="invoice.user_id">
<div><strong>Your Contact:</strong></div>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="invoice.user_id.image" t-attf-src="data:image/png;base64,#{invoice.user_id.image}" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not invoice.user_id.image" src="/web/static/src/img/placeholder.png" alt="Contact"/>
<span t-field="invoice.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
<a data-toggle="modal" href="#" data-target="#invoice_chatter">Contact us</a>
<div class="d-flex h-100">
<div class="align-self-center">
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="invoice.user_id.image" t-attf-src="data:image/png;base64,#{invoice.user_id.image}" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not invoice.user_id.image" src="/web/static/src/img/placeholder.png" alt="Contact"/>
</div>
<div class="align-self-center">
<span t-field="invoice.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
<a data-toggle="modal" href="#" data-target="#invoice_chatter"><i class="fa fa-comment"></i> Contact us</a>
</div>
</div>
</t>
<div t-if="not invoice.user_id" class="text-center mr16">
<a data-toggle="modal" href="#" data-target="#invoice_chatter">Contact us</a>
<a data-toggle="modal" href="#" data-target="#invoice_chatter"><i class="fa fa-comment"></i> Contact us</a>
</div>
</div>
<div class="text-center d-none d-md-block mt4 o_portal_brand">
Powered by <a target="_blank" href="http://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a>
</div>
</div>
<div class="text-center d-none d-md-block o_portal_brand">
Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a>
</div>
</div>
<!-- Page Content -->
<div id="invoice_content" class="col-lg-9 o_portal_page_content d-print-none">
<div t-ignore="true" class="float-md-right css_editable_mode_hidden d-print-none">
<ul class="list-inline">
<li groups="sales_team.group_sale_salesman" class="list-inline-item">
<a role="button" class="btn btn-info mt16 mr16" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % (invoice._name, invoice.id, invoice.env.ref('account.action_invoice_tree1').id)"><t t-if="invoice.state == 'draft'">Edit Invoice</t><t t-else="">Leave Portal</t></a>
</li>
</ul>
</div>
<t t-if="error or warning" t-call="account.portal_invoice_error"/>
<t t-if="success and (not error and not warning)" t-call="account.portal_invoice_success"/>
<div class="o_portal_html_view">
<iframe id="invoice_html" class="mt8 mb8" width="100%" height="100%" frameborder="0" scrolling="no" t-attf-src="/my/invoices/html/#{invoice.id}/#{invoice.access_token}?report_type=html"/>
<iframe id="invoice_html" class="mt8 mb8" width="100%" height="100%" frameborder="0" scrolling="no" t-att-src="invoice.get_portal_url(report_type='html')"/>
</div>
</div>
</div>
+2 -2
View File
@@ -10,7 +10,7 @@
name="account.report_invoice_with_payments"
file="account.report_invoice_with_payments"
attachment="(object.state in ('open','in_payment','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')"
print_report_name="(object._get_printed_report_name())"
print_report_name="(object._get_report_base_filename())"
groups="account.group_account_invoice"
/>
@@ -22,7 +22,7 @@
name="account.report_invoice"
file="account.report_invoice"
attachment="(object.state in ('open','in_payment','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')"
print_report_name="(object._get_printed_report_name())"
print_report_name="(object._get_report_base_filename())"
/>
<report
+30 -42
View File
@@ -5,9 +5,8 @@
<t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context({'lang': lang})"/>
<t t-set="address">
<address t-field="o.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
<div t-if="o.partner_id.vat" class="mt16"><t t-esc="o.company_id.country_id.vat_label or 'Tax ID'"/>: <span t-field="o.partner_id.vat"/></div>
<address t-field="o.partner_id" t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
<div t-if="o.partner_id.vat" class="mt16"><t t-esc="o.company_id.country_id.vat_label or 'Tax ID'"/>: <span t-field="o.partner_id.vat"/></div>
</t>
<div class="page">
<h2>
@@ -20,59 +19,55 @@
<span t-field="o.number"/>
</h2>
<!-- HTML Preview for mobile responsive -->
<t t-set="o_grid_layout" t-value="'col-2'"/>
<t t-if="type_html" t-set="o_grid_layout" t-value="'col-6 col-md-2'"/>
<div class="row mt32 mb32">
<div t-att-class="o_grid_layout" t-if="o.name" name="description">
<div class="row col mt32 mb32">
<div class="mr32" t-if="o.name" name="description">
<strong>Description:</strong>
<p t-field="o.name"/>
</div>
<div t-att-class="o_grid_layout" t-if="o.date_invoice" name="invoice_date">
<div class="mr32" t-if="o.date_invoice" name="invoice_date">
<strong>Invoice Date:</strong>
<p t-field="o.date_invoice"/>
</div>
<div t-att-class="o_grid_layout" t-if="o.date_due and o.type == 'out_invoice' and o.state in ('open', 'in_payment', 'paid')" name="due_date">
<div class="mr32" t-if="o.date_due and o.type == 'out_invoice' and o.state in ('open', 'in_payment', 'paid')" name="due_date">
<strong>Due Date:</strong>
<p t-field="o.date_due"/>
</div>
<div t-att-class="o_grid_layout" t-if="o.origin" name="origin">
<div class="mr32" t-if="o.origin" name="origin">
<strong>Source:</strong>
<p t-field="o.origin"/>
</div>
<div t-att-class="o_grid_layout" t-if="o.partner_id.ref" name="customer_code">
<div class="mr32" t-if="o.partner_id.ref" name="customer_code">
<strong>Customer Code:</strong>
<p t-field="o.partner_id.ref"/>
</div>
<div name="reference" t-att-class="o_grid_layout" t-if="o.reference">
<div name="reference" class="mr32" t-if="o.reference">
<strong>Reference:</strong>
<p t-field="o.reference"/>
</div>
</div>
<!-- Is there a discount on at least one line? -->
<t t-set="display_discount" t-value="any([l.discount for l in o.invoice_line_ids])"/>
<table t-attf-class="table table-sm" name="invoice_line_table">
<thead>
<tr>
<t t-set="colspan" t-value="6"/>
<th><span>Description</span></th>
<th class="d-none"><span>Source Document</span></th>
<th class="text-right"><span>Quantity</span></th>
<th class="text-right d-none d-md-table-cell"><span>Unit Price</span></th>
<th t-if="display_discount" class="text-right d-none d-md-table-cell">
<th class="text-left">Description</th>
<th class="d-none text-left">Source Document</th>
<th class="text-right">Quantity</th>
<th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Unit Price</th>
<th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span>Disc.(%)</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th class="text-right d-none d-md-table-cell"><span>Taxes</span></th>
<th t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Taxes</th>
<th class="text-right">
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
</th>
</tr>
</thead>
<tbody class="invoice_tbody">
<tbody class="invoice_tbody">
<t t-set="current_subtotal" t-value="0"/>
<t t-foreach="o.invoice_line_ids" t-as="line">
@@ -88,26 +83,18 @@
<span t-field="line.quantity"/>
<span t-field="line.uom_id" groups="uom.group_uom"/>
</td>
<td class="text-right d-none d-md-table-cell">
<span t-field="line.price_unit"/>
<td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td>
<td t-if="display_discount" class="text-right d-none d-md-table-cell">
<td t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-field="line.discount"/>
</td>
<td class="text-right d-none d-md-table-cell">
<td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.invoice_line_tax_ids))"/>
</td>
<td class="text-right">
<span
t-field="line.price_subtotal"
t-options='{"widget": "monetary", "display_currency": o.currency_id}'
groups="account.group_show_line_subtotals_tax_excluded"
/>
<span
t-field="line.price_total"
t-options='{"widget": "monetary", "display_currency": o.currency_id}'
groups="account.group_show_line_subtotals_tax_included"
/>
<span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
@@ -127,7 +114,8 @@
<t t-if="current_section and (line_last or o.invoice_line_ids[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="colspan">
<strong>Subtotal:</strong> <span
<strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": o.currency_id}'
/>
@@ -139,13 +127,13 @@
</table>
<div class="clearfix">
<div id="total" class="row justify-content-end">
<div t-attf-class="col-md-4 #{'col-4' if not type_html else ''}">
<table class="table table-sm" style="min-width: 250px;max-width: 400px;">
<div id="total" class="row">
<div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
<table class="table table-sm">
<tr class="border-black" style="border-bottom:1px solid #dddddd;">
<td><strong>Subtotal</strong></td>
<td class="text-right">
<span t-field="o.amount_untaxed" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
<span t-field="o.amount_untaxed"/>
</td>
</tr>
<t t-foreach="o.amount_by_group" t-as="amount_by_group">
@@ -172,7 +160,7 @@
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span t-field="o.amount_total" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
<span t-field="o.amount_total"/>
</td>
</tr>
</table>
@@ -219,7 +207,7 @@
<tr class="border-black">
<td><strong>Amount Due</strong></td>
<td class="text-right">
<span t-field="o.residual" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
<span t-field="o.residual"/>
</td>
</tr>
</t>
@@ -8,7 +8,7 @@
<t t-set="tx_ids" t-value="invoice.transaction_ids.filtered(lambda tx: tx.state in ('pending', 'authorized', 'done'))"/>
<t t-set="pending_manual_txs" t-value="tx_ids.filtered(lambda tx: tx.state == 'pending' and tx.acquirer_id.provider in ('transfer', 'manual'))"/>
<a t-if="invoice.state == 'open' and invoice.amount_total and invoice.type == 'out_invoice' and (pending_manual_txs or not tx_ids)"
t-att-href="'/my/invoices/%s#portal_pay' % invoice.id" title="Pay now" aria-label="Pay now" class="btn btn-sm btn-primary" role="button">
t-att-href="invoice.get_portal_url(anchor='portal_pay')" title="Pay now" aria-label="Pay now" class="btn btn-sm btn-primary" role="button">
<i class="fa fa-arrow-circle-right"/><span class='d-none d-md-inline'> Pay Now</span>
</a>
</td>
@@ -49,8 +49,8 @@
<t t-call="payment.payment_tokens_list">
<t t-set="mode" t-value="'payment'"/>
<t t-set="partner_id" t-value="invoice.partner_id.id"/>
<t t-set="success_url" t-value="'/my/invoices/%s?%s' % (invoice.id, keep_query())"/>
<t t-set="error_url" t-value="'/my/invoices/%s?%s' % (invoice.id, keep_query())"/>
<t t-set="success_url" t-value="invoice.get_portal_url()"/>
<t t-set="error_url" t-value="invoice.get_portal_url()"/>
<t t-set="access_token" t-value="access_token or ''"/>
<t t-set="callback_method" t-value="''"/>
<t t-set="form_action" t-value="'/invoice/pay/' + str(invoice.id) + '/s2s_token_tx/'"/>
+2 -2
View File
@@ -15,10 +15,10 @@ class AccountInvoice(models.Model):
amount_total_words = fields.Char("Total (In Words)", compute="_compute_amount_total_words")
def _get_printed_report_name(self):
def _get_report_base_filename(self):
self.ensure_one()
if self.company_id.country_id.code != 'IN':
return super(AccountInvoice, self)._get_printed_report_name()
return super(AccountInvoice, self)._get_report_base_filename()
return self.type == 'out_invoice' and self.state == 'draft' and _('Draft %s') % (self.journal_id.name) or \
self.type == 'out_invoice' and self.state in ('open','in_payment','paid') and '%s - %s' % (self.journal_id.name, self.number) or \
self.type == 'out_refund' and self.state == 'draft' and _('Credit Note') or \
+24 -2
View File
@@ -2,18 +2,20 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import math
import re
from werkzeug import urls
from odoo import fields as odoo_fields, tools, _
from odoo.exceptions import ValidationError, AccessError, MissingError
from odoo.http import Controller, request, route
from odoo.exceptions import ValidationError, AccessError, MissingError, UserError
from odoo.http import content_disposition, Controller, request, route
from odoo.tools import consteq
# --------------------------------------------------
# Misc tools
# --------------------------------------------------
def pager(url, total, page=1, step=30, scope=5, url_args=None):
""" Generate a dict with required value to render `website.pager` template. This method compute
url, page range to display, ... in the pager.
@@ -264,3 +266,23 @@ class CustomerPortal(Controller):
values.update(get_records_pager(history, document))
return values
def _show_report(self, model, report_type, report_ref, download=False):
if report_type not in ('html', 'pdf', 'text'):
raise UserError("Invalid report type: %s" % report_type)
report_sudo = request.env.ref(report_ref).sudo()
if not isinstance(report_sudo, type(request.env['ir.actions.report'])):
raise UserError("%s is not the reference of a report" % report_ref)
method_name = 'render_qweb_%s' % (report_type)
report = getattr(report_sudo, method_name)([model.id], data={'report_type': report_type})[0]
reporthttpheaders = [
('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'),
('Content-Length', len(report)),
]
if report_type == 'pdf' and download:
filename = "%s.pdf" % (re.sub('\W+', '-', model._get_report_base_filename()))
reporthttpheaders.append(('Content-Disposition', content_disposition(filename)))
return request.make_response(report, headers=reporthttpheaders)
+22
View File
@@ -145,3 +145,25 @@ class PortalMixin(models.AbstractModel):
"database.secret")
token = (self.env.cr.dbname, self.access_token, pid)
return hmac.new(secret.encode('utf-8'), repr(token).encode('utf-8'), hashlib.sha256).hexdigest()
@api.multi
def get_portal_url(self, suffix=None, report_type=None, download=None, query_string=None, anchor=None):
"""
Get a portal url for this model, including access_token.
The associated route must handle the flags for them to have any effect.
- suffix: string to append to the url, before the query string
- report_type: report_type query string, often one of: html, pdf, text
- download: set the download query string to true
- query_string: additional query string
- anchor: string to append after the anchor #
"""
self.ensure_one()
url = self.access_url + '%s?access_token=%s%s%s%s%s' % (
suffix if suffix else '',
self._portal_ensure_token(),
'&report_type=%s' % report_type if report_type else '',
'&download=true' if download else '',
query_string if query_string else '',
'#%s' % anchor if anchor else ''
)
return url
+1 -1
View File
@@ -57,7 +57,7 @@
</main>
<footer class="bg-light o_footer">
<div class="container mt16 mb16" id="footer">
<div class="float-right" t-ignore="true" t-if="not editable">
<div class="float-right mb16" t-ignore="true" t-if="not editable">
Powered by <a target="_blank" href="http://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a>
</div>
</div>
+9 -10
View File
@@ -73,7 +73,7 @@ class CustomerPortal(CustomerPortal):
)
# search the count to display, according to the pager data
quotations = SaleOrder.search(domain, order=sort_order, limit=self._items_per_page, offset=pager['offset'])
request.session['my_quotes_history'] = quotations.ids[:100]
request.session['my_quotations_history'] = quotations.ids[:100]
values.update({
'date': date_begin,
@@ -139,16 +139,14 @@ class CustomerPortal(CustomerPortal):
return request.render("sale.portal_my_orders", values)
@http.route(['/my/orders/<int:order_id>'], type='http', auth="public", website=True)
def portal_order_page(self, order_id, pdf=None, access_token=None, message=False, **kw):
def portal_order_page(self, order_id, report_type=None, access_token=None, message=False, download=False, **kw):
try:
order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token)
except (AccessError, MissingError):
return request.redirect('/my')
if pdf:
pdf = request.env.ref('sale.report_web_quote').sudo().with_context(set_viewport_size=True).render_qweb_pdf([order_sudo.id])[0]
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
if report_type in ('html', 'pdf', 'text'):
return self._show_report(model=order_sudo, report_type=report_type, report_ref='sale.report_web_quote', download=download)
# use sudo to allow accessing/viewing orders for public user
# only if he knows the private token
@@ -162,18 +160,16 @@ class CustomerPortal(CustomerPortal):
transaction = order_sudo.get_portal_last_transaction()
# TODO SEB fix breadcrumbs -> should always be visible (or at least no home alone)
values = {
'sale_order': order_sudo,
'message': int(message) if message else False,
'action': request.env.ref('sale.action_quotations').id,
'no_breadcrumbs': request.env.user.partner_id.commercial_partner_id not in order_sudo.message_partner_ids,
'tx_state': transaction.state if transaction else False,
'need_payment': order_sudo.invoice_status == 'to invoice' and transaction.state in ['draft', 'cancel'],
'token': access_token,
'return_url': '/shop/payment/validate',
'bootstrap_formatting': True,
'partner_id': order_sudo.partner_id.id,
'report_type': 'html',
}
if order_sudo.has_to_be_paid() or values['need_payment']:
@@ -189,7 +185,10 @@ class CustomerPortal(CustomerPortal):
[('partner_id', '=', order_sudo.partner_id.id),
('acquirer_id', 'in', [acq.id for acq in values['s2s_acquirers']])])
history = request.session.get('my_quotes_history', [])
if order_sudo.state in ('draft', 'sent', 'cancel'):
history = request.session.get('my_quotations_history', [])
else:
history = request.session.get('my_orders_history', [])
values.update(get_records_pager(history, order_sudo))
return request.render('sale.sale_order_portal_template', values)
+7 -7
View File
@@ -58,7 +58,7 @@
<field name="partner_id" ref="base.res_partner_4"/>
<field name="partner_invoice_id" ref="base.res_partner_address_13"/>
<field name="partner_shipping_id" ref="base.res_partner_address_13"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
@@ -86,7 +86,7 @@
<field name="partner_id" ref="base.res_partner_4"/>
<field name="partner_invoice_id" ref="base.res_partner_4"/>
<field name="partner_shipping_id" ref="base.res_partner_4"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
</record>
@@ -113,7 +113,7 @@
<field name="partner_id" ref="base.res_partner_3"/>
<field name="partner_invoice_id" ref="base.res_partner_address_25"/>
<field name="partner_shipping_id" ref="base.res_partner_address_25"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
</record>
@@ -177,7 +177,7 @@
<field name="partner_id" ref="base.res_partner_18"/>
<field name="partner_invoice_id" ref="base.res_partner_18"/>
<field name="partner_shipping_id" ref="base.res_partner_18"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
</record>
@@ -195,7 +195,7 @@
<field name="partner_id" ref="base.res_partner_3"/>
<field name="partner_invoice_id" ref="base.res_partner_address_11"/>
<field name="partner_shipping_id" ref="base.res_partner_address_11"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
</record>
@@ -621,7 +621,7 @@ Thanks!</field>
<field name="partner_id" ref="base.partner_demo_portal"/>
<field name="partner_invoice_id" ref="base.partner_demo_portal"/>
<field name="partner_shipping_id" ref="base.partner_demo_portal"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="state">sent</field>
<field name="team_id" ref="sales_team.team_sales_department"/>
@@ -660,7 +660,7 @@ Thanks!</field>
<field name="partner_id" ref="base.partner_demo_portal"/>
<field name="partner_invoice_id" ref="base.partner_demo_portal"/>
<field name="partner_shipping_id" ref="base.partner_demo_portal"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_admin"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
+14 -9
View File
@@ -179,6 +179,8 @@ class SaleOrder(models.Model):
help="Delivery date you can promise to the customer, computed from product lead times and from the shipping policy of the order.")
amount_undiscounted = fields.Float('Amount Before Discount', compute='_compute_amount_undiscounted', digits=0)
type_name = fields.Char('Type Name', compute='_compute_type_name')
transaction_ids = fields.Many2many('payment.transaction', 'sale_order_transaction_rel', 'sale_order_id', 'transaction_id',
string='Transactions', copy=False, readonly=True)
authorized_transaction_ids = fields.Many2many('payment.transaction', compute='_compute_authorized_transaction_ids',
@@ -237,6 +239,12 @@ class SaleOrder(models.Model):
total += line.price_subtotal + line.price_unit * ((line.discount or 0.0) / 100.0) * line.product_uom_qty # why is there a discount in a field named amount_undiscounted ??
self.amount_undiscounted = total
@api.multi
@api.depends('state')
def _compute_type_name(self):
for record in self:
record.type_name = _('Quotation') if self.state in ('draft', 'sent', 'cancel') else _('Sales Order')
@api.multi
def unlink(self):
for order in self:
@@ -818,15 +826,6 @@ class SaleOrder(models.Model):
def payment_action_void(self):
self.authorized_transaction_ids.s2s_void_transaction()
@api.multi
def get_portal_url(self, suffix=None):
"""
Get a portal url for this sale order, including access_token.
- suffix: string to append to the url, before the query string
"""
self.ensure_one()
return self.access_url + '%s?access_token=%s' % (suffix if suffix else '', self._portal_ensure_token())
@api.multi
def get_portal_last_transaction(self):
self.ensure_one()
@@ -836,6 +835,12 @@ class SaleOrder(models.Model):
def _get_customer_lead(self, product_tmpl_id):
return False
@api.multi
def _get_report_base_filename(self):
self.ensure_one()
return '%s %s' % (self.type_name, self.name)
class SaleOrderLine(models.Model):
_name = 'sale.order.line'
_description = 'Sales Order Line'
+18 -27
View File
@@ -37,28 +37,28 @@
<span t-field="doc.name"/>
</h2>
<div class="row mt32 mb32" id="informations">
<div t-if="doc.client_order_ref" class="col-3">
<div class="row col mt32 mb32" id="informations">
<div t-if="doc.client_order_ref" class="mr32">
<strong>Your Reference:</strong>
<p t-field="doc.client_order_ref"/>
</div>
<div t-if="doc.confirmation_date and doc.state not in ['draft','sent']" class="col-3">
<div t-if="doc.confirmation_date and doc.state not in ['draft','sent']" class="mr32">
<strong>Date Ordered:</strong>
<p t-field="doc.confirmation_date"/>
</div>
<div t-if="doc.date_order and doc.state in ['draft','sent']" class="col-3">
<div t-if="doc.date_order and doc.state in ['draft','sent']" class="mr32">
<strong>Quotation Date:</strong>
<p t-field="doc.date_order"/>
</div>
<div t-if="doc.user_id.name" class="col-3">
<div t-if="doc.user_id.name" class="mr32">
<strong>Salesperson:</strong>
<p t-field="doc.user_id"/>
</div>
<div name="payment_term" t-if="doc.payment_term_id" class="col-3">
<div name="payment_term" t-if="doc.payment_term_id" class="mr32">
<strong>Payment Terms:</strong>
<p t-field="doc.payment_term_id"/>
</div>
<div t-if="doc.validity_date and doc.state in ['draft', 'sent']" class="col-3">
<div t-if="doc.validity_date and doc.state in ['draft', 'sent']" class="mr32">
<strong>Expiration Date:</strong>
<p t-field="doc.validity_date"/>
</div>
@@ -71,14 +71,14 @@
<thead>
<tr>
<t t-set="colspan" t-value="5"/>
<th>Description</th>
<th class="text-left">Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span>Disc.(%)</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th class="text-right">Taxes</th>
<th class="text-left">Taxes</th>
<th class="text-right">
<t groups="account.group_show_line_subtotals_tax_excluded">Amount</t>
<t groups="account.group_show_line_subtotals_tax_included">Total Price</t>
@@ -107,20 +107,12 @@
<td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span t-field="line.discount"/>
</td>
<td class="text-right">
<td class="text-left">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td>
<td class="text-right">
<span
t-field="line.price_subtotal"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
groups="account.group_show_line_subtotals_tax_excluded"
/>
<span
t-field="line.price_total"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
groups="account.group_show_line_subtotals_tax_included"
/>
<span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
@@ -140,7 +132,8 @@
<t t-if="current_section and (line_last or doc.order_line[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="colspan">
<strong>Subtotal:</strong> <span
<strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/>
@@ -153,13 +146,12 @@
<div class="clearfix">
<div class="row" name="total">
<div class="col-4 ml-auto">
<table class="table table-sm" style="min-width: 200px;max-width: 350px;">
<div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
<table class="table table-sm">
<tr class="border-black" style="border-bottom:1px solid #dddddd;">
<td><strong>Subtotal</strong></td>
<td class="text-right">
<span t-field="doc.amount_untaxed"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
<span t-field="doc.amount_untaxed"/>
</td>
</tr>
<t t-foreach="doc.amount_by_group" t-as="amount_by_group">
@@ -188,8 +180,7 @@
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span t-field="doc.amount_total"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
<span t-field="doc.amount_total"/>
</td>
</tr>
</table>
+2 -2
View File
@@ -10,10 +10,10 @@
}
tr.o_is_line_note {
color: #999;
font-style: italic;
}
tr.o_is_line_section {
font-weight: bold;
background-color: #EEE;
background-color: #DDDDDD;
}
+199 -199
View File
@@ -3,18 +3,17 @@
<template id="portal_my_home_menu_sale" name="Portal layout : sales menu entries" inherit_id="portal.portal_layout" priority="20">
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
<li t-if="page_name == 'quote' or sales_order and sales_order.state in ('sent', 'cancel')" class="breadcrumb-item">
<a t-if="sales_order" t-attf-href="/my/quotes?{{ keep_query() }}">Quotations</a>
<li t-if="page_name == 'quote' or sale_order and sale_order.state in ('sent', 'cancel')" class="breadcrumb-item">
<a t-if="sale_order" t-attf-href="/my/quotes?{{ keep_query() }}">Quotations</a>
<t t-else="">Quotations</t>
</li>
<li t-if="page_name == 'order' or sales_order and sales_order.state not in ('sent', 'cancel')" class="breadcrumb-item">
<a t-if="sales_order" t-attf-href="/my/orders?{{ keep_query() }}">Sales Orders</a>
<li t-if="page_name == 'order' or sale_order and sale_order.state not in ('sent', 'cancel')" class="breadcrumb-item">
<a t-if="sale_order" t-attf-href="/my/orders?{{ keep_query() }}">Sales Orders</a>
<t t-else="">Sales Orders</t>
</li>
<li t-if="sales_order" class="breadcrumb-item">
<t t-if="sales_order.state in ('sent', 'cancel')">Quotation</t>
<t t-else="">Order</t>
<t t-esc="sales_order.name"/>
<li t-if="sale_order" class="breadcrumb-item">
<span t-field="sale_order.type_name"/>
<t t-esc="sale_order.name"/>
</li>
</xpath>
</template>
@@ -67,7 +66,7 @@
</t>
</td>
<td>
<span t-field="quotation.amount_total" t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
<span t-field="quotation.amount_total"/>
</td>
</tr>
</t>
@@ -114,7 +113,7 @@
<span class="badge badge-success d-none d-md-inline-block"><i class="fa fa-fw fa-check" role="img" aria-label="Done" title="Done"></i><span class="d-none d-md-inline"> Done</span></span>
</t>
</td>
<td><span t-field="order.amount_total" t-options='{"widget": "monetary", "display_currency": order.pricelist_id.currency_id}'/></td>
<td><span t-field="order.amount_total"/></td>
</tr>
</t>
</table></div>
@@ -131,16 +130,22 @@
<div class="row mt16 o_portal_sale_sidebar">
<!-- Sidebar -->
<div class="col-lg-3 d-print-none">
<div class="bs-sidebar">
<!-- TODO SEB add sale order total here, like invoice for the style -->
<div class="bs-sidebar mb16">
<div class="text-center pt16 o_account_portal_sidebar">
<h2 class="media-heading">
<strong>
<span t-field="sale_order.amount_total"/>
</strong>
</h2>
</div>
<t t-if="sale_order.team_id.team_type != 'website'">
<div class="text-center" t-if="sale_order.state in ('draft', 'sent', 'waiting_date')" style="padding: 10px">
<t t-set="show_hr" t-value="False"/>
<a role="button" t-if="not sale_order.is_expired and not sale_order.signature and sale_order.has_to_be_signed()" class="btn btn-success btn-block" data-toggle="modal" data-target="#modalaccept" href="#">
<a role="button" t-if="not sale_order.is_expired and not sale_order.signature and sale_order.has_to_be_signed()" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept" href="#">
<i class="fa fa-check"/> Accept
<t t-set="show_hr" t-value="True"/>
</a>
<a role="button" t-elif="not sale_order.is_expired and sale_order.has_to_be_paid()" class="btn btn-success btn-block" data-toggle="modal" data-target="#modalaccept" href="#">
<a role="button" t-elif="not sale_order.is_expired and sale_order.has_to_be_paid()" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept" href="#">
<i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept &amp; </t>Pay
<t t-set="show_hr" t-value="True"/>
</a>
@@ -152,7 +157,7 @@
<hr class="mb0" t-if="show_hr"/>
</div>
<div class="text-center" t-if="need_payment or (sale_order.state in ('sale') and sale_order.has_to_be_paid() and tx_state != 'done')" style="padding: 10px">
<a role="button" class="btn btn-success btn-block" data-toggle="modal" data-target="#modalaccept" href="#">
<a role="button" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept" href="#">
<i class="fa fa-check"></i> Pay Now
</a>
<hr class="mb0"/>
@@ -185,23 +190,20 @@
<hr class="mb0"/>
<p class="text-muted mb8">Your advantage:</p>
<t t-if="sale_order.amount_untaxed == sale_order.amount_total">
<strong t-field="sale_order.amount_total"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
<strong t-field="sale_order.amount_total"/>
<strong t-field="sale_order.amount_undiscounted"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
style="text-decoration: line-through"
class="text-danger"/>
</t>
<t t-if="sale_order.amount_untaxed != sale_order.amount_total">
<strong t-field="sale_order.amount_untaxed"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
<strong t-field="sale_order.amount_untaxed"/>
<strong t-field="sale_order.amount_undiscounted"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
style="text-decoration: line-through"
class="text-danger"/>
<br />
(<span t-field="sale_order.amount_total"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/> Incl. tax)
(<span t-field="sale_order.amount_total"/> Incl. tax)
</t>
</div>
</div>
@@ -209,12 +211,21 @@
<div class="pb8 ml16 mr16" t-if="sale_order.user_id">
<hr class="mb0"/>
<div><strong>Your Contact:</strong></div>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="sale_order.user_id.image" t-attf-src="data:image/png;base64,#{sale_order.user_id.image}" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not sale_order.user_id.image" src="/web/static/src/img/placeholder.png" alt="Contact"/>
<span t-field="sale_order.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
<a href="#discussion"><i class="fa fa-comment"></i> Contact us</a>
<div class="d-flex h-100">
<div class="align-self-center">
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="sale_order.user_id.image" t-attf-src="data:image/png;base64,#{sale_order.user_id.image}" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not sale_order.user_id.image" src="/web/static/src/img/placeholder.png" alt="Contact"/>
</div>
<div class="align-self-center">
<span t-field="sale_order.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
<a href="#discussion"><i class="fa fa-comment"></i> Contact us</a>
</div>
</div>
</div>
</div>
<div class="text-center d-none d-md-block o_portal_brand">
Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a>
</div>
</div>
<!-- Page content -->
<div id="quote_content" class="col-lg-9">
@@ -258,13 +269,13 @@
<span>I agree that by signing this proposal, I accept it on the behalf of </span>
<b t-field="sale_order.partner_id.commercial_partner_id"/>
<span>, for an amount of </span>
<b data-id="total_amount" t-field="sale_order.amount_total" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
<b data-id="total_amount" t-field="sale_order.amount_total"/>
<span>with payment terms: </span><b t-field="sale_order.payment_term_id"/>.
</p>
<t t-call="portal.portal_signature">
<t t-set="object" t-value="sale_order"/>
<t t-set="partner_name" t-value="sale_order.partner_id.name"/>
<t t-set="callUrl" t-value="sale_order.get_portal_url('/accept')"/>
<t t-set="callUrl" t-value="sale_order.get_portal_url(suffix='/accept')"/>
<t t-set="accessToken" t-value="sale_order.access_token"/>
</t>
</main>
@@ -281,7 +292,7 @@
<span>I agree that by paying this proposal, I accept it on the behalf of </span>
<b t-field="sale_order.partner_id.commercial_partner_id"/><span>
, for an amount of </span>
<b data-id="total_amount" t-field="sale_order.amount_total" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
<b data-id="total_amount" t-field="sale_order.amount_total"/>
<span>with payment terms: </span><b t-field="sale_order.payment_term_id"/>.
</p>
</main>
@@ -292,8 +303,8 @@
<t t-set="mode" t-value="'payment'"/>
<t t-set="submit_txt" t-value="'Pay &amp; Confirm'"/>
<t t-set="icon_class" t-value="'fa-lock'"/>
<t t-set="form_action" t-value="sale_order.get_portal_url('/transaction/token')"/>
<t t-set="prepare_tx_url" t-value="sale_order.get_portal_url('/transaction/')"/>
<t t-set="form_action" t-value="sale_order.get_portal_url(suffix='/transaction/token')"/>
<t t-set="prepare_tx_url" t-value="sale_order.get_portal_url(suffix='/transaction/')"/>
<t t-set="access_token" t-value="sale_order.access_token"/>
</t>
</div>
@@ -332,22 +343,28 @@
<t t-if="sale_order.team_id.team_type != 'website'">
<div class="text-center d-print-none mt16 mb16" id="quotation_toolbar" t-if="sale_order.state in ('draft', 'sent', 'waiting_date')">
<div class="row justify-content-center text-center d-print-none mt8 mb16" t-if="sale_order.state in ('draft', 'sent', 'waiting_date')">
<t t-set="show_buttons" t-value="False"/>
<a role="button" class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-if="not sale_order.is_expired and not sale_order.signature and sale_order.has_to_be_signed()" href="#">
<i class="fa fa-check"></i> Accept
<t t-set="show_buttons" t-value="True"/>
</a>
<a role="button" class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-elif="not sale_order.is_expired and sale_order.has_to_be_paid()" href="#">
<i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept &amp; </t>Pay
<t t-set="show_buttons" t-value="True"/>
</a>
<a role="button" class="btn btn-info" type="submit" href="#discussion" t-if="show_buttons">
<i class="fa fa-comment"></i> Feedback
</a>
<a role="button" class="btn btn-danger" data-toggle="modal" data-target="#modaldecline" href="#" t-if="show_buttons">
<i class="fa fa-times"></i> Reject
</a>
<div class="col-sm-auto mt8">
<a role="button" class="btn btn-primary" data-toggle="modal" data-target="#modalaccept" t-if="not sale_order.is_expired and not sale_order.signature and sale_order.has_to_be_signed()" href="#">
<i class="fa fa-check"></i> Accept
<t t-set="show_buttons" t-value="True"/>
</a>
<a role="button" class="btn btn-primary" data-toggle="modal" data-target="#modalaccept" t-elif="not sale_order.is_expired and sale_order.has_to_be_paid()" href="#">
<i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept &amp; </t>Pay
<t t-set="show_buttons" t-value="True"/>
</a>
</div>
<div class="col-sm-auto mt8">
<a role="button" class="btn btn-secondary" type="submit" href="#discussion" t-if="show_buttons">
<i class="fa fa-comment"></i> Feedback
</a>
</div>
<div class="col-sm-auto mt8">
<a role="button" class="btn btn-danger" data-toggle="modal" data-target="#modaldecline" href="#" t-if="show_buttons">
<i class="fa fa-times"></i> Reject
</a>
</div>
</div>
<div class="text-center" t-if="need_payment or (sale_order.state in ('sale') and sale_order.has_to_be_paid() and tx_state != 'done')" style="padding: 10px">
<a role="button" class="btn btn-primary" data-toggle="modal" data-target="#modalaccept" href="#">
@@ -376,17 +393,18 @@
<!-- Intro -->
<div id="introduction" class="page-header mt16">
<h1>
<t t-if="sale_order.state in ('draft','sent','cancel')">Quotation</t>
<t t-if="sale_order.state not in ('draft','sent','cancel')">Sales Order</t>
<span t-field="sale_order.type_name"></span>
<em t-esc="sale_order.name"/>
<div t-ignore="true" class="float-md-right css_editable_mode_hidden d-print-none">
<ul class="list-inline">
<li groups="sales_team.group_sale_salesman" class="list-inline-item">
<a role="button" t-if="sale_order.state not in ('manual')" class="btn btn-info" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % (sale_order._name, sale_order.id, action)">Edit Quotation</a>
<a role="button" t-if="sale_order.state in ('manual')" class=" btn btn-info" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % (sale_order._name, sale_order.id, action)">Back to Sales Order</a>
<a role="button" class="btn btn-info" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % (sale_order._name, sale_order.id, sale_order.env.ref('sale.action_quotations').id)">
<t t-if="sale_order.state in ('draft', 'sent', 'cancel')">Edit Quotation</t>
<t t-else="">Edit Sales Order</t>
</a>
</li>
<li class="list-inline-item">
<a role="button" class="btn btn-info" target="_blank" t-att-href="sale_order.get_portal_url() + '&amp;pdf=True'">Print</a>
<a role="button" class="btn btn-info" target="_blank" t-att-href="sale_order.get_portal_url(report_type='pdf')">Print</a>
</li>
</ul>
</div>
@@ -434,7 +452,7 @@
</div>
<div>
<t t-foreach="invoices" t-as="i">
<t t-set="report_url" t-value="'/my/invoices/pdf/%s?%s' % (i.id, 'access_token=%s' % i.access_token if access_token else '')"/>
<t t-set="report_url" t-value="i.get_portal_url(report_type='pdf', download=True)"/>
<div>
<a t-att-href="report_url"><span class="fa fa-download" role="img" aria-label="Download" title="Download"/></a>
<a t-att-href="report_url"><span t-esc="i.number"/></a>
@@ -452,168 +470,150 @@
</div>
</div>
</div>
<div class="oe_structure"/>
<!-- Pricing -->
<!-- Signature -->
<section id="details">
<section id="details" style="page-break-inside: auto;">
<h1 class="page-header" id="details">Details</h1>
<t t-set="display_taxamount" t-value="any([line.tax_id for line in sale_order.order_line])"/>
<t t-set="display_discount" t-value="True in [line.discount > 0 for line in sale_order.order_line]"/>
<div style="overflow-x:auto;">
<table class="table" id="sales_order_table">
<thead>
<tr>
<t t-set="colspan" t-value="6"/>
<th></th>
<th>Products</th>
<th>Quantity</th>
<th t-if="display_taxamount">
<span>Taxes</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th t-if="display_discount">
<span>Discount</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th class="text-right">Unit Price</th>
<th class="text-right" >
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
</th>
</tr>
</thead>
<tbody class="sale_tbody">
<table class="table" id="sales_order_table">
<thead>
<tr>
<t t-set="colspan" t-value="7"/>
<th class="d-none d-xl-table-cell"></th>
<th class="text-left">Products</th>
<th class="text-left">Quantity</th>
<th t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">Unit Price</th>
<th t-if="display_discount" class="text-right">
<span>Discount</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Taxes</th>
<th class="text-right" >
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
</th>
</tr>
</thead>
<tbody class="sale_tbody">
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal" t-value="0"/>
<t t-foreach="sale_order.order_line" t-as="line">
<t t-foreach="sale_order.order_line" t-as="line">
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<tr t-attf-class="{{ 'o_is_' + line.display_type if line.display_type else '' }}">
<t t-if="not line.display_type">
<td class="text-center">
<div class="d-none d-xl-block">
<span t-field="line.product_image" t-options="{'widget': 'image', 'class': 'rounded', 'style':'width: 48px;height:48px'}" />
</div>
</td>
<td id="product_name"><span t-field="line.name"/></td>
<td>
<div id="quote_qty">
<span t-field="line.product_uom_qty"/>
<span t-field="line.product_uom" groups="uom.group_uom"/>
</div>
</td>
<td t-if="display_taxamount">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td>
<td t-if="display_discount">
<strong t-if="line.discount" class="text-info">
<t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount
</strong>
</td>
<td class="text-right">
<div
t-field="line.price_unit"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
t-att-style="line.discount and 'text-decoration: line-through' or None"
t-att-class="(line.discount and 'text-danger' or '') + ' text-right'"
/>
<div t-if="line.discount">
<t t-esc="(1-line.discount / 100.0) * line.price_unit" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</div>
</td>
<td class="text-right">
<span
t-field="line.price_subtotal"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
groups="account.group_show_line_subtotals_tax_excluded"
/>
<span
t-field="line.price_total"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
groups="account.group_show_line_subtotals_tax_included"
/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
<td t-att-colspan="colspan">
<span t-field="line.name"/>
</td>
<t t-set="current_section" t-value="line"/>
<t t-set="current_subtotal" t-value="0"/>
</t>
<t t-if="line.display_type == 'line_note'">
<td t-att-colspan="colspan">
<span t-field="line.name"/>
</td>
</t>
</tr>
<t t-if="current_section and (line_last or sale_order.order_line[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="colspan">
<strong>Subtotal:</strong>
<span
t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
</div>
<div class="clearfix">
<div id="total" class="row" name="total">
<div class="col-8 col-sm-6 col-md-4 ml-auto">
<table class="table table-sm">
<tr class="border-black" style="border-bottom:1px solid #dddddd;">
<td><strong>Subtotal</strong></td>
<tr t-attf-class="{{ 'o_is_' + line.display_type if line.display_type else '' }}">
<t t-if="not line.display_type">
<td class="text-center d-none d-xl-table-cell">
<div>
<span t-field="line.product_image" t-options="{'widget': 'image', 'class': 'rounded', 'style':'width: 48px;height:48px'}" />
</div>
</td>
<td id="product_name"><span t-field="line.name"/></td>
<td>
<div id="quote_qty">
<span t-field="line.product_uom_qty"/>
<span t-field="line.product_uom" groups="uom.group_uom"/>
</div>
</td>
<td t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">
<div
t-field="line.price_unit"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
t-att-style="line.discount and 'text-decoration: line-through' or None"
t-att-class="(line.discount and 'text-danger' or '') + ' text-right'"
/>
<div t-if="line.discount">
<t t-esc="(1-line.discount / 100.0) * line.price_unit" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</div>
</td>
<td t-if="display_discount">
<strong t-if="line.discount" class="text-info text-right">
<t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount
</strong>
</td>
<td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td>
<td class="text-right">
<span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
<td t-att-colspan="colspan">
<span t-field="line.name"/>
</td>
<t t-set="current_section" t-value="line"/>
<t t-set="current_subtotal" t-value="0"/>
</t>
<t t-if="line.display_type == 'line_note'">
<td t-att-colspan="colspan">
<span t-field="line.name"/>
</td>
</t>
</tr>
<t t-if="current_section and (line_last or sale_order.order_line[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="colspan">
<strong class="mr16">Subtotal</strong>
<span
data-id="total_amount"
t-field="sale_order.amount_untaxed"
t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'
t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
/>
</td>
</tr>
<t t-foreach="sale_order.amount_by_group" t-as="amount_by_group">
<tr style="border-bottom:1px solid #dddddd;">
<t t-if="amount_by_group[3] == 1 and sale_order.amount_untaxed == amount_by_group[2]">
<td>
<span t-esc="amount_by_group[0]"/>
<span>&amp;nbsp;<span>on</span>&amp;nbsp;<t t-esc="amount_by_group[2]" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/></span>
</td>
<td class="text-right">
<span t-esc="amount_by_group[1]"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</td>
</t>
<t t-else ="">
<td>
<span t-esc="amount_by_group[0]"/>
</td>
<td class="text-right">
<span t-esc="amount_by_group[1]"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</td>
</t>
</tr>
</t>
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span data-id="total_amount" t-field="sale_order.amount_total" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</td>
</t>
</t>
</tbody>
</table>
<div id="total" class="row" name="total" style="page-break-inside: avoid;">
<div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
<table class="table table-sm">
<tr class="border-black" style="border-bottom:1px solid #dddddd;">
<td><strong>Subtotal</strong></td>
<td class="text-right">
<span
data-id="total_amount"
t-field="sale_order.amount_untaxed"
t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'
/>
</td>
</tr>
<t t-foreach="sale_order.amount_by_group" t-as="amount_by_group">
<tr style="border-bottom:1px solid #dddddd;">
<t t-if="amount_by_group[3] == 1 and sale_order.amount_untaxed == amount_by_group[2]">
<td>
<span t-esc="amount_by_group[0]"/>
<span>&amp;nbsp;<span>on</span>&amp;nbsp;<t t-esc="amount_by_group[2]" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/></span>
</td>
<td class="text-right">
<span t-esc="amount_by_group[1]"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</td>
</t>
<t t-else ="">
<td>
<span t-esc="amount_by_group[0]"/>
</td>
<td class="text-right">
<span t-esc="amount_by_group[1]"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</td>
</t>
</tr>
</table>
</div>
</t>
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span data-id="total_amount" t-field="sale_order.amount_total" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
</td>
</tr>
</table>
</div>
</div>
</section>
+2 -2
View File
@@ -54,7 +54,7 @@ class CustomerPortal(CustomerPortal):
return request.redirect('/my')
if order_sudo.state != 'sent':
return request.redirect(order_sudo.get_portal_url() + "&message=4")
return request.redirect(order_sudo.get_portal_url(query_string="&message=4"))
order_sudo.action_cancel()
message = post.get('decline_message')
if message:
@@ -95,7 +95,7 @@ class CustomerPortal(CustomerPortal):
option_sudo.add_option_to_order()
return request.redirect(option_sudo.order_id.get_portal_url() + "#details")
return request.redirect(option_sudo.order_id.get_portal_url(anchor='details'))
# note dbo: website_sale code
@http.route(['/my/orders/<int:order_id>/transaction/'], type='json', auth="public", website=True)
+1 -1
View File
@@ -163,7 +163,7 @@ class SaleOrder(models.Model):
self.ensure_one()
if self.state not in ['sale', 'done']:
auth_param = url_encode(self.partner_id.signup_get_auth_param()[self.partner_id.id])
return self.get_portal_url() + '&%s' % auth_param
return self.get_portal_url(query_string='&%s' % auth_param)
return super(SaleOrder, self)._get_share_url(redirect, signup_partner, pid)
def get_portal_confirmation_action(self):
@@ -1,25 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_saleorder_validity_date" inherit_id="sale.report_saleorder_document">
<template id="report_saleorder_document_inherit_sale_management" inherit_id="sale.report_saleorder_document">
<xpath expr="//div[hasclass('page')]/p[@id='fiscal_position_remark']" position="after">
<div t-if="doc.sale_order_option_ids and doc.state in ['draft', 'sent']">
<h2>
<h4>
<span>Optional Products</span>
</h2>
</h4>
<table class="table table-sm">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
<th groups="sale.group_discount_per_so_line"></th>
<th class="text-left">Description</th>
<th groups="sale.group_discount_per_so_line" class="text-left"></th>
<th class="text-right">Unit Price</th>
</tr>
</thead>
<tbody class="sale_tbody">
<tr t-foreach="doc.sale_order_option_ids" t-as="option">
<td>
<span t-field="option.product_id.name"/>
</td>
<td>
<span t-field="option.name"/>
</td>
@@ -21,7 +21,7 @@
<xpath expr="//section[@id='details']//t[@t-if='not line.display_type']" position="inside">
<td class="text-center" t-if="display_remove">
<a t-att-href="sale_order.get_portal_url('/update_line/%s' % line.id) + '&amp;unlink=True'" class="mb8 js_update_line_json d-print-none" t-if="line.sale_order_option_ids" aria-label="Remove" title="Remove">
<a t-att-href="sale_order.get_portal_url(suffix='/update_line/%s' % line.id, query_string='&amp;unlink=True')" class="mb8 js_update_line_json d-print-none" t-if="line.sale_order_option_ids" aria-label="Remove" title="Remove">
<span class="fa fa-trash-o"></span>
</a>
</td>
@@ -31,16 +31,16 @@
<t t-if="any([(not option.line_id) for option in sale_order.sale_order_option_ids])">
<section>
<h1 class="page-header">Optional Products</h1>
</section>
<section>
<div style="overflow-x:auto;">
<t t-set="display_discount" t-value="True in [option.discount for option in sale_order.sale_order_option_ids]"/>
<table class="table table-hover">
<thead>
<tr>
<th>Product</th>
<th></th>
<th class="text-left">Product</th>
<th t-if="display_discount" class="text-right">
<span>Discount</span>
</th>
<th class="text-right">Price</th>
<th t-if="sale_order.state in ['draft', 'sent']"></th>
<th t-if="sale_order.state in ['draft', 'sent'] and report_type == 'html'"></th>
</tr>
</thead>
<tbody>
@@ -49,8 +49,8 @@
<td>
<div t-field="option.name"/>
</td>
<td>
<strong t-if="option.discount" class="text-info">
<td t-if="display_discount">
<strong t-if="option.discount" class="text-info text-right">
<t t-esc="((option.discount % 1) and '%s' or '%d') % option.discount"/>% discount
</strong>
</td>
@@ -65,8 +65,8 @@
</div>
</strong>
</td>
<td class="text-center" t-if="sale_order.state in ['draft', 'sent']">
<a t-att-href="sale_order.get_portal_url('/add_option/%s' % option.id)" class="mb8 d-print-none" aria-label="Add to cart" title="Add to cart">
<td class="text-center" t-if="sale_order.state in ['draft', 'sent'] and report_type == 'html'">
<a t-att-href="sale_order.get_portal_url(suffix='/add_option/%s' % option.id)" class="mb8 d-print-none" aria-label="Add to cart" title="Add to cart">
<span class="fa fa-shopping-cart"/>
</a>
</td>
@@ -74,7 +74,6 @@
</tr>
</tbody>
</table>
</div>
</section>
</t>
</xpath>
@@ -85,7 +84,7 @@
<div class="input-group-prepend d-print-none">
<span class="input-group-text">
<a t-att-href="sale_order.get_portal_url('/update_line/%s' % line.id) + '&amp;remove=True'" class="mb8 js_update_line_json" aria-label="Remove one" title="Remove one">
<a t-att-href="sale_order.get_portal_url(suffix='/update_line/%s' % line.id, query_string='&amp;remove=True')" class="mb8 js_update_line_json" aria-label="Remove one" title="Remove one">
<span class="fa fa-minus"/>
</a>
</span>
@@ -94,7 +93,7 @@
<input type="text" class="js_quantity form-control" t-att-data-id="line.id" t-att-value="line.product_uom_qty"/>
<div class="input-group-append d-print-none">
<span class="input-group-text">
<a t-att-href="sale_order.get_portal_url('/update_line/%s' % line.id)" class="mb8 js_update_line_json" aria-label="Add one" title="Add one">
<a t-att-href="sale_order.get_portal_url(suffix='/update_line/%s' % line.id)" class="mb8 js_update_line_json" aria-label="Add one" title="Add one">
<span class="fa fa-plus"/>
</a>
</span>
@@ -41,7 +41,7 @@
tbody {
color: gray('700');
tr:nth-child(odd) {
background-color: rgba(184, 171, 180, 0.2);
background-color: rgba(220, 205, 216, 0.2);
}
tr td {
border-top: none;
@@ -71,8 +71,4 @@
color: #fff;
}
}
.table > tbody > tr.o_is_line_section td {
border-color: black;
}
}
-18
View File
@@ -18,12 +18,6 @@
}
}
&.table {
td, th {
vertical-align: middle;
}
}
thead {
color: $o-main-text-color;
border-bottom: 1px solid #cacaca;
@@ -118,18 +112,6 @@
border-top: none; // Override bootstrap for grouped list views
}
tbody {
tr:focus-within {
background-color: $o-form-lightsecondary;
}
tr{
.focussed {
background-color: $o-form-lightsecondary;
}
}
}
// Values styling
.o_list_number {
text-align: right;
+2 -4
View File
@@ -52,12 +52,10 @@ body {
.row.o_is_line_section,
.table > tbody > tr.o_is_line_section td {
font-weight: bold;
background-color: #EEE;
padding: 8px;
border: 1px solid #BBB;
background-color: #DDDDDD;
}
.row.o_is_line_note,
tr.o_is_line_note {
color: #999;
font-style: italic;
}
+7 -7
View File
@@ -146,7 +146,7 @@
<!-- External layouts styles -->
<template id="address_layout">
<t t-set="colclass" t-value="'col-5 offset-7'"/>
<t t-set="colclass" t-value="('col-md-5' if report_type == 'html' else 'col-5') + ' ml-auto'"/>
<t t-if="address">
<div class="row">
<t t-if="information_block">
@@ -192,7 +192,7 @@
<li t-if="company.vat" class="list-inline-item"><i class="fa fa-building-o" role="img" aria-label="Fiscal number"/><t t-esc="company.country_id.vat_label or 'Tax ID'"/>: <span t-field="company.vat"/></li>
</ul>
<div t-field="company.report_footer"/>
<div t-if="not type_html" class="text-muted">
<div t-if="report_type == 'pdf'" class="text-muted">
Page:
<span class="page"/>
of
@@ -232,7 +232,7 @@
<li t-if="company.vat" class="list-inline-item"><t t-esc="company.country_id.vat_label or 'Tax ID'"/>: <span t-field="company.vat"/></li>
</ul>
<div t-field="company.report_footer"/>
<div t-if="not type_html">
<div t-if="report_type == 'pdf'">
Page: <span class="page"/> / <span class="topage"/>
</div>
</div>
@@ -275,7 +275,7 @@
<h4 class="mt0 mb0 text-uppercase" t-field="company.report_header"/>
</div>
<div class="col-1">
<ul t-if="not type_html" class="list-inline pagenumber float-right text-center">
<ul t-if="report_type != 'html'" class="list-inline pagenumber float-right text-center">
<li class="list-inline-item"><strong><span class="page"/></strong></li>
</ul>
</div>
@@ -298,9 +298,9 @@
</div>
<div class="row">
<div class="col-6" name="company_address">
<span t-field="company.partner_id"
<div t-field="company.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'
style="border-bottom: 1px solid black; display:inline-block;"/>
/>
</div>
</div>
</div>
@@ -323,7 +323,7 @@
<span t-field="company.report_footer"/>
</div>
<div t-if="not type_html" class="text-muted">
<div t-if="report_type == 'pdf'" class="text-muted">
Page: <span class="page"/> / <span class="topage"/>
</div>
</div>
@@ -22,12 +22,13 @@
</t>
<div itemprop="address" itemscope="itemscope" itemtype="http://schema.org/PostalAddress">
<div t-if="address and 'address' in fields">
<i t-if="not options.get('no_marker')" class='fa fa-map-marker fa-fw' role="img" aria-label="Address" title="Address"/> <span itemprop="streetAddress" t-raw="address.replace('\n', options.get('no_tag_br') and ', ' or ('&lt;br/&gt;%s' % ('' if options.get('no_marker') else '&amp;nbsp; &amp;nbsp; ')))"/>
<i t-if="not options.get('no_marker')" class='fa fa-map-marker fa-fw' role="img" aria-label="Address" title="Address"/>
<span itemprop="streetAddress" t-raw="address.replace('\n', options.get('no_tag_br') and ', ' or ('&lt;br/&gt;%s' % ('' if options.get('no_marker') else '&amp;nbsp; &amp;nbsp; ')))"/>
</div>
<div t-if="city and 'city' in fields">
<i t-if="not options.get('no_marker')" class='fa fa-map-marker fa-fw' role="img" aria-label="Address" title="Address"/>
<span itemprop="addressLocality" t-raw="city"/>,
<span itemprop="addressCountry" t-raw="country_id"/>
<span itemprop="addressLocality" t-esc="city"/>,
<span itemprop="addressCountry" t-esc="country_id"/>
</div>
<div t-if="phone and 'phone' in fields"><i t-if="not options.get('no_marker') or options.get('phone_icons')" class='fa fa-phone fa-fw' role="img" aria-label="Phone" title="Phone"/> <span itemprop="telephone" t-esc="phone"/></div>
<div t-if="mobile and 'mobile' in fields"><i t-if="not options.get('no_marker') or options.get('phone_icons')" class='fa fa-mobile fa-fw' role="img" aria-label="Mobile" title="Mobile"/> <span itemprop="telephone" t-esc="mobile"/></div>