[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 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # 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 import http, _
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.exceptions import AccessError, MissingError 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) 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) @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): def portal_my_invoices(self, page=1, date_begin=None, date_end=None, sortby=None, **kw):
values = self._prepare_portal_layout_values() values = self._prepare_portal_layout_values()
partner = request.env.user.partner_id
AccountInvoice = request.env['account.invoice'] AccountInvoice = request.env['account.invoice']
domain = [] domain = []
@@ -82,42 +77,18 @@ class PortalAccount(CustomerPortal):
return request.render("account.portal_my_invoices", values) return request.render("account.portal_my_invoices", values)
@http.route(['/my/invoices/<int:invoice_id>'], type='http', auth="public", website=True) @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: try:
invoice_sudo = self._document_check_access('account.invoice', invoice_id, access_token) invoice_sudo = self._document_check_access('account.invoice', invoice_id, access_token)
except (AccessError, MissingError): except (AccessError, MissingError):
return request.redirect('/my') 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) values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw)
return request.render("account.portal_invoice_page", values) 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 # My Home
# ------------------------------------------------------------ # ------------------------------------------------------------
+1 -10
View File
@@ -479,7 +479,7 @@ class AccountInvoice(models.Model):
sequence.number_next = int(result.group(2)) sequence.number_next = int(result.group(2))
@api.multi @api.multi
def _get_printed_report_name(self): def _get_report_base_filename(self):
self.ensure_one() self.ensure_one()
return self.type == 'out_invoice' and self.state == 'draft' and _('Draft Invoice') or \ 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 \ 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), len(res),
) for r in 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 @api.multi
def preview_invoice(self): def preview_invoice(self):
self.ensure_one() self.ensure_one()
@@ -186,5 +186,5 @@ class ReportInvoiceWithPayment(models.AbstractModel):
'doc_ids': docids, 'doc_ids': docids,
'doc_model': report.model, 'doc_model': report.model,
'docs': self.env[report.model].browse(docids), '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; var self = this;
this._super.apply(this, arguments); this._super.apply(this, arguments);
var $invoiceHtml = this.$el.find('iframe#invoice_html'); 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 * Called when the iframe is loaded or the window is resized on customer portal.
* here we set height and width of html preview (iframe) * The goal is to expand the iframe height to display the full report without scrollbar.
* *
* @private * @private
* @param {object} $el: the iframe
*/ */
_onLoadIframe: function ($el) { _updateIframeSize: function ($el) {
var $body = $el.contents().find('body'); var $body = $el.contents().find('body');
// expand iframe height for display full report on the custome portal (no scrollbar in iframe) // Set it to 0 first to handle the case where scrollHeight is too big for its content.
$el.height($body.scrollParent().height()); $el.height(0);
// removed extra space in html preview (specially left and right margin) $el.height($body[0].scrollHeight);
$body.width('100%');
}, },
/** /**
* @private * @private
@@ -3,13 +3,14 @@
// section and note on sale order and invoice. // 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,
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 { 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, table.o_section_and_note_list_view tr.o_data_row.o_is_line_section,
div.oe_kanban_card.o_is_line_section { div.oe_kanban_card.o_is_line_section {
font-weight: bold; font-weight: bold;
background-color: #EEE; background-color: #DDDDDD;
} }
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section { table.o_section_and_note_list_view tr.o_data_row.o_is_line_section {
border-top: 1px solid #BBB; 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="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="quantity"/> <field name="quantity"/>
<field name="uom_id" groups="uom.group_uom"/> <field name="uom_id" groups="uom.group_uom"/>
<field name="price_unit"/> <field name="price_unit" string="Price"/>
<field name="discount" groups="base.group_no_one"/> <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'}" <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)]"/> domain="[('type_tax_use','=','sale'),('company_id', '=', parent.company_id)]"/>
<field name="price_subtotal" string="Subtotal" groups="account.group_show_line_subtotals_tax_excluded"/> <field name="price_subtotal" string="Subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
@@ -6,7 +6,8 @@
<t t-else="">Invoices</t> <t t-else="">Invoices</t>
</li> </li>
<li t-if="invoice" class="breadcrumb-item"> <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> </li>
</xpath> </xpath>
</template> </template>
@@ -42,7 +43,10 @@
<t t-foreach="invoices" t-as="invoice"> <t t-foreach="invoices" t-as="invoice">
<tr> <tr>
<td> <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>
<td><span t-field="invoice.date_invoice"/></td> <td><span t-field="invoice.date_invoice"/></td>
<td class='d-none d-md-table-cell'><span t-field="invoice.date_due"/></td> <td class='d-none d-md-table-cell'><span t-field="invoice.date_due"/></td>
@@ -85,34 +89,47 @@
</div> </div>
<div class="o_payment_button"> <div class="o_payment_button">
<div class="text-center mb8 ml16 mr16 o_download_pdf"> <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 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="'/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 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>
</div> </div>
<hr t-if="invoice.user_id" class="mt0 mb0"/> <hr t-if="invoice.user_id" class="mt0 mb0"/>
<div class="pb8 ml16 mr16 container"> <div class="pb8 ml16 mr16 container">
<t t-if="invoice.user_id"> <t t-if="invoice.user_id">
<div><strong>Your Contact:</strong></div> <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"/> <div class="d-flex h-100">
<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 class="align-self-center">
<span t-field="invoice.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/> <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"/>
<a data-toggle="modal" href="#" data-target="#invoice_chatter">Contact us</a> <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> </t>
<div t-if="not invoice.user_id" class="text-center mr16"> <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> </div>
<div class="text-center d-none d-md-block mt4 o_portal_brand"> </div>
Powered by <a target="_blank" href="http://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a> <div class="text-center d-none d-md-block o_portal_brand">
</div> Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a>
</div> </div>
</div> </div>
<!-- Page Content --> <!-- Page Content -->
<div id="invoice_content" class="col-lg-9 o_portal_page_content d-print-none"> <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="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"/> <t t-if="success and (not error and not warning)" t-call="account.portal_invoice_success"/>
<div class="o_portal_html_view"> <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> </div>
</div> </div>
+2 -2
View File
@@ -10,7 +10,7 @@
name="account.report_invoice_with_payments" name="account.report_invoice_with_payments"
file="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')" 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" groups="account.group_account_invoice"
/> />
@@ -22,7 +22,7 @@
name="account.report_invoice" name="account.report_invoice"
file="account.report_invoice" file="account.report_invoice"
attachment="(object.state in ('open','in_payment','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')" 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 <report
+30 -42
View File
@@ -5,9 +5,8 @@
<t t-call="web.external_layout"> <t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context({'lang': lang})"/> <t t-set="o" t-value="o.with_context({'lang': lang})"/>
<t t-set="address"> <t t-set="address">
<address t-field="o.partner_id" <address t-field="o.partner_id" t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
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>
<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> </t>
<div class="page"> <div class="page">
<h2> <h2>
@@ -20,59 +19,55 @@
<span t-field="o.number"/> <span t-field="o.number"/>
</h2> </h2>
<!-- HTML Preview for mobile responsive --> <div class="row col mt32 mb32">
<t t-set="o_grid_layout" t-value="'col-2'"/> <div class="mr32" t-if="o.name" name="description">
<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">
<strong>Description:</strong> <strong>Description:</strong>
<p t-field="o.name"/> <p t-field="o.name"/>
</div> </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> <strong>Invoice Date:</strong>
<p t-field="o.date_invoice"/> <p t-field="o.date_invoice"/>
</div> </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> <strong>Due Date:</strong>
<p t-field="o.date_due"/> <p t-field="o.date_due"/>
</div> </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> <strong>Source:</strong>
<p t-field="o.origin"/> <p t-field="o.origin"/>
</div> </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> <strong>Customer Code:</strong>
<p t-field="o.partner_id.ref"/> <p t-field="o.partner_id.ref"/>
</div> </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> <strong>Reference:</strong>
<p t-field="o.reference"/> <p t-field="o.reference"/>
</div> </div>
</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])"/> <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"> <table t-attf-class="table table-sm" name="invoice_line_table">
<thead> <thead>
<tr> <tr>
<t t-set="colspan" t-value="6"/> <t t-set="colspan" t-value="6"/>
<th><span>Description</span></th> <th class="text-left">Description</th>
<th class="d-none"><span>Source Document</span></th> <th class="d-none text-left">Source Document</th>
<th class="text-right"><span>Quantity</span></th> <th class="text-right">Quantity</th>
<th class="text-right d-none d-md-table-cell"><span>Unit Price</span></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" class="text-right d-none d-md-table-cell"> <th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span>Disc.(%)</span> <span>Disc.(%)</span>
<t t-set="colspan" t-value="colspan+1"/> <t t-set="colspan" t-value="colspan+1"/>
</th> </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"> <th class="text-right">
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span> <span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span> <span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody class="invoice_tbody"> <tbody class="invoice_tbody">
<t t-set="current_subtotal" t-value="0"/> <t t-set="current_subtotal" t-value="0"/>
<t t-foreach="o.invoice_line_ids" t-as="line"> <t t-foreach="o.invoice_line_ids" t-as="line">
@@ -88,26 +83,18 @@
<span t-field="line.quantity"/> <span t-field="line.quantity"/>
<span t-field="line.uom_id" groups="uom.group_uom"/> <span t-field="line.uom_id" groups="uom.group_uom"/>
</td> </td>
<td class="text-right d-none d-md-table-cell"> <td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-field="line.price_unit"/> <span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td> </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"/> <span t-field="line.discount"/>
</td> </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))"/> <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.invoice_line_tax_ids))"/>
</td> </td>
<td class="text-right"> <td class="text-right">
<span <span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
t-field="line.price_subtotal" <span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
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"
/>
</td> </td>
</t> </t>
<t t-if="line.display_type == 'line_section'"> <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')"> <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"> <tr class="is-subtotal text-right">
<td t-att-colspan="colspan"> <td t-att-colspan="colspan">
<strong>Subtotal:</strong> <span <strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal" t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": o.currency_id}' t-options='{"widget": "monetary", "display_currency": o.currency_id}'
/> />
@@ -139,13 +127,13 @@
</table> </table>
<div class="clearfix"> <div class="clearfix">
<div id="total" class="row justify-content-end"> <div id="total" class="row">
<div t-attf-class="col-md-4 #{'col-4' if not type_html else ''}"> <div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
<table class="table table-sm" style="min-width: 250px;max-width: 400px;"> <table class="table table-sm">
<tr class="border-black" style="border-bottom:1px solid #dddddd;"> <tr class="border-black" style="border-bottom:1px solid #dddddd;">
<td><strong>Subtotal</strong></td> <td><strong>Subtotal</strong></td>
<td class="text-right"> <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> </td>
</tr> </tr>
<t t-foreach="o.amount_by_group" t-as="amount_by_group"> <t t-foreach="o.amount_by_group" t-as="amount_by_group">
@@ -172,7 +160,7 @@
<tr class="border-black"> <tr class="border-black">
<td><strong>Total</strong></td> <td><strong>Total</strong></td>
<td class="text-right"> <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> </td>
</tr> </tr>
</table> </table>
@@ -219,7 +207,7 @@
<tr class="border-black"> <tr class="border-black">
<td><strong>Amount Due</strong></td> <td><strong>Amount Due</strong></td>
<td class="text-right"> <td class="text-right">
<span t-field="o.residual" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/> <span t-field="o.residual"/>
</td> </td>
</tr> </tr>
</t> </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="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'))"/> <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)" <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> <i class="fa fa-arrow-circle-right"/><span class='d-none d-md-inline'> Pay Now</span>
</a> </a>
</td> </td>
@@ -49,8 +49,8 @@
<t t-call="payment.payment_tokens_list"> <t t-call="payment.payment_tokens_list">
<t t-set="mode" t-value="'payment'"/> <t t-set="mode" t-value="'payment'"/>
<t t-set="partner_id" t-value="invoice.partner_id.id"/> <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="success_url" t-value="invoice.get_portal_url()"/>
<t t-set="error_url" t-value="'/my/invoices/%s?%s' % (invoice.id, keep_query())"/> <t t-set="error_url" t-value="invoice.get_portal_url()"/>
<t t-set="access_token" t-value="access_token or ''"/> <t t-set="access_token" t-value="access_token or ''"/>
<t t-set="callback_method" t-value="''"/> <t t-set="callback_method" t-value="''"/>
<t t-set="form_action" t-value="'/invoice/pay/' + str(invoice.id) + '/s2s_token_tx/'"/> <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") 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() self.ensure_one()
if self.company_id.country_id.code != 'IN': 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 \ 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_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 \ 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. # Part of Odoo. See LICENSE file for full copyright and licensing details.
import math import math
import re
from werkzeug import urls from werkzeug import urls
from odoo import fields as odoo_fields, tools, _ from odoo import fields as odoo_fields, tools, _
from odoo.exceptions import ValidationError, AccessError, MissingError from odoo.exceptions import ValidationError, AccessError, MissingError, UserError
from odoo.http import Controller, request, route from odoo.http import content_disposition, Controller, request, route
from odoo.tools import consteq from odoo.tools import consteq
# -------------------------------------------------- # --------------------------------------------------
# Misc tools # Misc tools
# -------------------------------------------------- # --------------------------------------------------
def pager(url, total, page=1, step=30, scope=5, url_args=None): 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 """ Generate a dict with required value to render `website.pager` template. This method compute
url, page range to display, ... in the pager. url, page range to display, ... in the pager.
@@ -264,3 +266,23 @@ class CustomerPortal(Controller):
values.update(get_records_pager(history, document)) values.update(get_records_pager(history, document))
return values 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") "database.secret")
token = (self.env.cr.dbname, self.access_token, pid) token = (self.env.cr.dbname, self.access_token, pid)
return hmac.new(secret.encode('utf-8'), repr(token).encode('utf-8'), hashlib.sha256).hexdigest() 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> </main>
<footer class="bg-light o_footer"> <footer class="bg-light o_footer">
<div class="container mt16 mb16" id="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> Powered by <a target="_blank" href="http://www.odoo.com?utm_source=db&amp;utm_medium=portal">Odoo</a>
</div> </div>
</div> </div>
+9 -10
View File
@@ -73,7 +73,7 @@ class CustomerPortal(CustomerPortal):
) )
# search the count to display, according to the pager data # 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']) 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({ values.update({
'date': date_begin, 'date': date_begin,
@@ -139,16 +139,14 @@ class CustomerPortal(CustomerPortal):
return request.render("sale.portal_my_orders", values) return request.render("sale.portal_my_orders", values)
@http.route(['/my/orders/<int:order_id>'], type='http', auth="public", website=True) @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: try:
order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token) order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token)
except (AccessError, MissingError): except (AccessError, MissingError):
return request.redirect('/my') return request.redirect('/my')
if pdf: if report_type in ('html', 'pdf', 'text'):
pdf = request.env.ref('sale.report_web_quote').sudo().with_context(set_viewport_size=True).render_qweb_pdf([order_sudo.id])[0] return self._show_report(model=order_sudo, report_type=report_type, report_ref='sale.report_web_quote', download=download)
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
# use sudo to allow accessing/viewing orders for public user # use sudo to allow accessing/viewing orders for public user
# only if he knows the private token # only if he knows the private token
@@ -162,18 +160,16 @@ class CustomerPortal(CustomerPortal):
transaction = order_sudo.get_portal_last_transaction() transaction = order_sudo.get_portal_last_transaction()
# TODO SEB fix breadcrumbs -> should always be visible (or at least no home alone)
values = { values = {
'sale_order': order_sudo, 'sale_order': order_sudo,
'message': int(message) if message else False, '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, 'tx_state': transaction.state if transaction else False,
'need_payment': order_sudo.invoice_status == 'to invoice' and transaction.state in ['draft', 'cancel'], 'need_payment': order_sudo.invoice_status == 'to invoice' and transaction.state in ['draft', 'cancel'],
'token': access_token, 'token': access_token,
'return_url': '/shop/payment/validate', 'return_url': '/shop/payment/validate',
'bootstrap_formatting': True, 'bootstrap_formatting': True,
'partner_id': order_sudo.partner_id.id, 'partner_id': order_sudo.partner_id.id,
'report_type': 'html',
} }
if order_sudo.has_to_be_paid() or values['need_payment']: 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), [('partner_id', '=', order_sudo.partner_id.id),
('acquirer_id', 'in', [acq.id for acq in values['s2s_acquirers']])]) ('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)) values.update(get_records_pager(history, order_sudo))
return request.render('sale.sale_order_portal_template', values) 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_id" ref="base.res_partner_4"/>
<field name="partner_invoice_id" ref="base.res_partner_address_13"/> <field name="partner_invoice_id" ref="base.res_partner_address_13"/>
<field name="partner_shipping_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="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/> <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')"/> <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_id" ref="base.res_partner_4"/>
<field name="partner_invoice_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="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="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/> <field name="team_id" ref="sales_team.team_sales_department"/>
</record> </record>
@@ -113,7 +113,7 @@
<field name="partner_id" ref="base.res_partner_3"/> <field name="partner_id" ref="base.res_partner_3"/>
<field name="partner_invoice_id" ref="base.res_partner_address_25"/> <field name="partner_invoice_id" ref="base.res_partner_address_25"/>
<field name="partner_shipping_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="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/> <field name="team_id" ref="sales_team.team_sales_department"/>
</record> </record>
@@ -177,7 +177,7 @@
<field name="partner_id" ref="base.res_partner_18"/> <field name="partner_id" ref="base.res_partner_18"/>
<field name="partner_invoice_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="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="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.crm_team_1"/> <field name="team_id" ref="sales_team.crm_team_1"/>
</record> </record>
@@ -195,7 +195,7 @@
<field name="partner_id" ref="base.res_partner_3"/> <field name="partner_id" ref="base.res_partner_3"/>
<field name="partner_invoice_id" ref="base.res_partner_address_11"/> <field name="partner_invoice_id" ref="base.res_partner_address_11"/>
<field name="partner_shipping_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="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/> <field name="team_id" ref="sales_team.team_sales_department"/>
</record> </record>
@@ -621,7 +621,7 @@ Thanks!</field>
<field name="partner_id" ref="base.partner_demo_portal"/> <field name="partner_id" ref="base.partner_demo_portal"/>
<field name="partner_invoice_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="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="pricelist_id" ref="product.list0"/>
<field name="state">sent</field> <field name="state">sent</field>
<field name="team_id" ref="sales_team.team_sales_department"/> <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_id" ref="base.partner_demo_portal"/>
<field name="partner_invoice_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="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="pricelist_id" ref="product.list0"/>
<field name="team_id" ref="sales_team.team_sales_department"/> <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')"/> <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.") 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) 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', transaction_ids = fields.Many2many('payment.transaction', 'sale_order_transaction_rel', 'sale_order_id', 'transaction_id',
string='Transactions', copy=False, readonly=True) string='Transactions', copy=False, readonly=True)
authorized_transaction_ids = fields.Many2many('payment.transaction', compute='_compute_authorized_transaction_ids', 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 ?? 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 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 @api.multi
def unlink(self): def unlink(self):
for order in self: for order in self:
@@ -818,15 +826,6 @@ class SaleOrder(models.Model):
def payment_action_void(self): def payment_action_void(self):
self.authorized_transaction_ids.s2s_void_transaction() 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 @api.multi
def get_portal_last_transaction(self): def get_portal_last_transaction(self):
self.ensure_one() self.ensure_one()
@@ -836,6 +835,12 @@ class SaleOrder(models.Model):
def _get_customer_lead(self, product_tmpl_id): def _get_customer_lead(self, product_tmpl_id):
return False 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): class SaleOrderLine(models.Model):
_name = 'sale.order.line' _name = 'sale.order.line'
_description = 'Sales Order Line' _description = 'Sales Order Line'
+18 -27
View File
@@ -37,28 +37,28 @@
<span t-field="doc.name"/> <span t-field="doc.name"/>
</h2> </h2>
<div class="row mt32 mb32" id="informations"> <div class="row col mt32 mb32" id="informations">
<div t-if="doc.client_order_ref" class="col-3"> <div t-if="doc.client_order_ref" class="mr32">
<strong>Your Reference:</strong> <strong>Your Reference:</strong>
<p t-field="doc.client_order_ref"/> <p t-field="doc.client_order_ref"/>
</div> </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> <strong>Date Ordered:</strong>
<p t-field="doc.confirmation_date"/> <p t-field="doc.confirmation_date"/>
</div> </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> <strong>Quotation Date:</strong>
<p t-field="doc.date_order"/> <p t-field="doc.date_order"/>
</div> </div>
<div t-if="doc.user_id.name" class="col-3"> <div t-if="doc.user_id.name" class="mr32">
<strong>Salesperson:</strong> <strong>Salesperson:</strong>
<p t-field="doc.user_id"/> <p t-field="doc.user_id"/>
</div> </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> <strong>Payment Terms:</strong>
<p t-field="doc.payment_term_id"/> <p t-field="doc.payment_term_id"/>
</div> </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> <strong>Expiration Date:</strong>
<p t-field="doc.validity_date"/> <p t-field="doc.validity_date"/>
</div> </div>
@@ -71,14 +71,14 @@
<thead> <thead>
<tr> <tr>
<t t-set="colspan" t-value="5"/> <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">Quantity</th>
<th class="text-right">Unit Price</th> <th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line"> <th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span>Disc.(%)</span> <span>Disc.(%)</span>
<t t-set="colspan" t-value="colspan+1"/> <t t-set="colspan" t-value="colspan+1"/>
</th> </th>
<th class="text-right">Taxes</th> <th class="text-left">Taxes</th>
<th class="text-right"> <th class="text-right">
<t groups="account.group_show_line_subtotals_tax_excluded">Amount</t> <t groups="account.group_show_line_subtotals_tax_excluded">Amount</t>
<t groups="account.group_show_line_subtotals_tax_included">Total Price</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"> <td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span t-field="line.discount"/> <span t-field="line.discount"/>
</td> </td>
<td class="text-right"> <td class="text-left">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/> <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td> </td>
<td class="text-right"> <td class="text-right">
<span <span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
t-field="line.price_subtotal" <span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
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"
/>
</td> </td>
</t> </t>
<t t-if="line.display_type == 'line_section'"> <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')"> <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"> <tr class="is-subtotal text-right">
<td t-att-colspan="colspan"> <td t-att-colspan="colspan">
<strong>Subtotal:</strong> <span <strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal" t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}' t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/> />
@@ -153,13 +146,12 @@
<div class="clearfix"> <div class="clearfix">
<div class="row" name="total"> <div class="row" name="total">
<div class="col-4 ml-auto"> <div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
<table class="table table-sm" style="min-width: 200px;max-width: 350px;"> <table class="table table-sm">
<tr class="border-black" style="border-bottom:1px solid #dddddd;"> <tr class="border-black" style="border-bottom:1px solid #dddddd;">
<td><strong>Subtotal</strong></td> <td><strong>Subtotal</strong></td>
<td class="text-right"> <td class="text-right">
<span t-field="doc.amount_untaxed" <span t-field="doc.amount_untaxed"/>
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
</td> </td>
</tr> </tr>
<t t-foreach="doc.amount_by_group" t-as="amount_by_group"> <t t-foreach="doc.amount_by_group" t-as="amount_by_group">
@@ -188,8 +180,7 @@
<tr class="border-black"> <tr class="border-black">
<td><strong>Total</strong></td> <td><strong>Total</strong></td>
<td class="text-right"> <td class="text-right">
<span t-field="doc.amount_total" <span t-field="doc.amount_total"/>
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
</td> </td>
</tr> </tr>
</table> </table>
+2 -2
View File
@@ -10,10 +10,10 @@
} }
tr.o_is_line_note { tr.o_is_line_note {
color: #999; font-style: italic;
} }
tr.o_is_line_section { tr.o_is_line_section {
font-weight: bold; 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"> <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"> <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"> <li t-if="page_name == 'quote' or sale_order and sale_order.state in ('sent', 'cancel')" class="breadcrumb-item">
<a t-if="sales_order" t-attf-href="/my/quotes?{{ keep_query() }}">Quotations</a> <a t-if="sale_order" t-attf-href="/my/quotes?{{ keep_query() }}">Quotations</a>
<t t-else="">Quotations</t> <t t-else="">Quotations</t>
</li> </li>
<li t-if="page_name == 'order' or sales_order and sales_order.state not in ('sent', 'cancel')" class="breadcrumb-item"> <li t-if="page_name == 'order' or sale_order and sale_order.state not in ('sent', 'cancel')" class="breadcrumb-item">
<a t-if="sales_order" t-attf-href="/my/orders?{{ keep_query() }}">Sales Orders</a> <a t-if="sale_order" t-attf-href="/my/orders?{{ keep_query() }}">Sales Orders</a>
<t t-else="">Sales Orders</t> <t t-else="">Sales Orders</t>
</li> </li>
<li t-if="sales_order" class="breadcrumb-item"> <li t-if="sale_order" class="breadcrumb-item">
<t t-if="sales_order.state in ('sent', 'cancel')">Quotation</t> <span t-field="sale_order.type_name"/>
<t t-else="">Order</t> <t t-esc="sale_order.name"/>
<t t-esc="sales_order.name"/>
</li> </li>
</xpath> </xpath>
</template> </template>
@@ -67,7 +66,7 @@
</t> </t>
</td> </td>
<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> </td>
</tr> </tr>
</t> </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> <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> </t>
</td> </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> </tr>
</t> </t>
</table></div> </table></div>
@@ -131,16 +130,22 @@
<div class="row mt16 o_portal_sale_sidebar"> <div class="row mt16 o_portal_sale_sidebar">
<!-- Sidebar --> <!-- Sidebar -->
<div class="col-lg-3 d-print-none"> <div class="col-lg-3 d-print-none">
<div class="bs-sidebar"> <div class="bs-sidebar mb16">
<!-- TODO SEB add sale order total here, like invoice for the style --> <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'"> <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"> <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"/> <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 <i class="fa fa-check"/> Accept
<t t-set="show_hr" t-value="True"/> <t t-set="show_hr" t-value="True"/>
</a> </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 <i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept &amp; </t>Pay
<t t-set="show_hr" t-value="True"/> <t t-set="show_hr" t-value="True"/>
</a> </a>
@@ -152,7 +157,7 @@
<hr class="mb0" t-if="show_hr"/> <hr class="mb0" t-if="show_hr"/>
</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"> <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 <i class="fa fa-check"></i> Pay Now
</a> </a>
<hr class="mb0"/> <hr class="mb0"/>
@@ -185,23 +190,20 @@
<hr class="mb0"/> <hr class="mb0"/>
<p class="text-muted mb8">Your advantage:</p> <p class="text-muted mb8">Your advantage:</p>
<t t-if="sale_order.amount_untaxed == sale_order.amount_total"> <t t-if="sale_order.amount_untaxed == sale_order.amount_total">
<strong t-field="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_undiscounted" <strong t-field="sale_order.amount_undiscounted"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}' t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
style="text-decoration: line-through" style="text-decoration: line-through"
class="text-danger"/> class="text-danger"/>
</t> </t>
<t t-if="sale_order.amount_untaxed != sale_order.amount_total"> <t t-if="sale_order.amount_untaxed != sale_order.amount_total">
<strong t-field="sale_order.amount_untaxed" <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_undiscounted" <strong t-field="sale_order.amount_undiscounted"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}' t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
style="text-decoration: line-through" style="text-decoration: line-through"
class="text-danger"/> class="text-danger"/>
<br /> <br />
(<span t-field="sale_order.amount_total" (<span t-field="sale_order.amount_total"/> Incl. tax)
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/> Incl. tax)
</t> </t>
</div> </div>
</div> </div>
@@ -209,12 +211,21 @@
<div class="pb8 ml16 mr16" t-if="sale_order.user_id"> <div class="pb8 ml16 mr16" t-if="sale_order.user_id">
<hr class="mb0"/> <hr class="mb0"/>
<div><strong>Your Contact:</strong></div> <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"/> <div class="d-flex h-100">
<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 class="align-self-center">
<span t-field="sale_order.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/> <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"/>
<a href="#discussion"><i class="fa fa-comment"></i> Contact us</a> <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> </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> </div>
<!-- Page content --> <!-- Page content -->
<div id="quote_content" class="col-lg-9"> <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> <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"/> <b t-field="sale_order.partner_id.commercial_partner_id"/>
<span>, for an amount of </span> <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"/>. <span>with payment terms: </span><b t-field="sale_order.payment_term_id"/>.
</p> </p>
<t t-call="portal.portal_signature"> <t t-call="portal.portal_signature">
<t t-set="object" t-value="sale_order"/> <t t-set="object" t-value="sale_order"/>
<t t-set="partner_name" t-value="sale_order.partner_id.name"/> <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 t-set="accessToken" t-value="sale_order.access_token"/>
</t> </t>
</main> </main>
@@ -281,7 +292,7 @@
<span>I agree that by paying this proposal, I accept it on the behalf of </span> <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> <b t-field="sale_order.partner_id.commercial_partner_id"/><span>
, for an amount of </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"/>. <span>with payment terms: </span><b t-field="sale_order.payment_term_id"/>.
</p> </p>
</main> </main>
@@ -292,8 +303,8 @@
<t t-set="mode" t-value="'payment'"/> <t t-set="mode" t-value="'payment'"/>
<t t-set="submit_txt" t-value="'Pay &amp; Confirm'"/> <t t-set="submit_txt" t-value="'Pay &amp; Confirm'"/>
<t t-set="icon_class" t-value="'fa-lock'"/> <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="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('/transaction/')"/> <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 t-set="access_token" t-value="sale_order.access_token"/>
</t> </t>
</div> </div>
@@ -332,22 +343,28 @@
<t t-if="sale_order.team_id.team_type != 'website'"> <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"/> <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="#"> <div class="col-sm-auto mt8">
<i class="fa fa-check"></i> Accept <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="#">
<t t-set="show_buttons" t-value="True"/> <i class="fa fa-check"></i> Accept
</a> <t t-set="show_buttons" t-value="True"/>
<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="#"> </a>
<i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept &amp; </t>Pay <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="#">
<t t-set="show_buttons" t-value="True"/> <i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept &amp; </t>Pay
</a> <t t-set="show_buttons" t-value="True"/>
<a role="button" class="btn btn-info" type="submit" href="#discussion" t-if="show_buttons"> </a>
<i class="fa fa-comment"></i> Feedback </div>
</a> <div class="col-sm-auto mt8">
<a role="button" class="btn btn-danger" data-toggle="modal" data-target="#modaldecline" href="#" t-if="show_buttons"> <a role="button" class="btn btn-secondary" type="submit" href="#discussion" t-if="show_buttons">
<i class="fa fa-times"></i> Reject <i class="fa fa-comment"></i> Feedback
</a> </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>
<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"> <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="#"> <a role="button" class="btn btn-primary" data-toggle="modal" data-target="#modalaccept" href="#">
@@ -376,17 +393,18 @@
<!-- Intro --> <!-- Intro -->
<div id="introduction" class="page-header mt16"> <div id="introduction" class="page-header mt16">
<h1> <h1>
<t t-if="sale_order.state in ('draft','sent','cancel')">Quotation</t> <span t-field="sale_order.type_name"></span>
<t t-if="sale_order.state not in ('draft','sent','cancel')">Sales Order</t>
<em t-esc="sale_order.name"/> <em t-esc="sale_order.name"/>
<div t-ignore="true" class="float-md-right css_editable_mode_hidden d-print-none"> <div t-ignore="true" class="float-md-right css_editable_mode_hidden d-print-none">
<ul class="list-inline"> <ul class="list-inline">
<li groups="sales_team.group_sale_salesman" class="list-inline-item"> <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" 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)">
<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> <t t-if="sale_order.state in ('draft', 'sent', 'cancel')">Edit Quotation</t>
<t t-else="">Edit Sales Order</t>
</a>
</li> </li>
<li class="list-inline-item"> <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> </li>
</ul> </ul>
</div> </div>
@@ -434,7 +452,7 @@
</div> </div>
<div> <div>
<t t-foreach="invoices" t-as="i"> <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> <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 class="fa fa-download" role="img" aria-label="Download" title="Download"/></a>
<a t-att-href="report_url"><span t-esc="i.number"/></a> <a t-att-href="report_url"><span t-esc="i.number"/></a>
@@ -452,168 +470,150 @@
</div> </div>
</div> </div>
</div> </div>
<div class="oe_structure"/>
<!-- Pricing --> <section id="details" style="page-break-inside: auto;">
<!-- Signature -->
<section id="details">
<h1 class="page-header" id="details">Details</h1> <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]"/> <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">
<table class="table" id="sales_order_table"> <thead>
<thead> <tr>
<tr> <t t-set="colspan" t-value="7"/>
<t t-set="colspan" t-value="6"/> <th class="d-none d-xl-table-cell"></th>
<th></th> <th class="text-left">Products</th>
<th>Products</th> <th class="text-left">Quantity</th>
<th>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_taxamount"> <th t-if="display_discount" class="text-right">
<span>Taxes</span> <span>Discount</span>
<t t-set="colspan" t-value="colspan+1"/> <t t-set="colspan" t-value="colspan+1"/>
</th> </th>
<th t-if="display_discount"> <th t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Taxes</th>
<span>Discount</span> <th class="text-right" >
<t t-set="colspan" t-value="colspan+1"/> <span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
</th> <span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
<th class="text-right">Unit Price</th> </th>
<th class="text-right" > </tr>
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span> </thead>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span> <tbody class="sale_tbody">
</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_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_total" groups="account.group_show_line_subtotals_tax_included"/>
<tr t-attf-class="{{ 'o_is_' + line.display_type if line.display_type else '' }}"> <tr t-attf-class="{{ 'o_is_' + line.display_type if line.display_type else '' }}">
<t t-if="not line.display_type"> <t t-if="not line.display_type">
<td class="text-center"> <td class="text-center d-none d-xl-table-cell">
<div class="d-none d-xl-block"> <div>
<span t-field="line.product_image" t-options="{'widget': 'image', 'class': 'rounded', 'style':'width: 48px;height:48px'}" /> <span t-field="line.product_image" t-options="{'widget': 'image', 'class': 'rounded', 'style':'width: 48px;height:48px'}" />
</div> </div>
</td> </td>
<td id="product_name"><span t-field="line.name"/></td> <td id="product_name"><span t-field="line.name"/></td>
<td> <td>
<div id="quote_qty"> <div id="quote_qty">
<span t-field="line.product_uom_qty"/> <span t-field="line.product_uom_qty"/>
<span t-field="line.product_uom" groups="uom.group_uom"/> <span t-field="line.product_uom" groups="uom.group_uom"/>
</div> </div>
</td> </td>
<td t-if="display_taxamount"> <td t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/> <div
</td> t-field="line.price_unit"
<td t-if="display_discount"> t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
<strong t-if="line.discount" class="text-info"> t-att-style="line.discount and 'text-decoration: line-through' or None"
<t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount t-att-class="(line.discount and 'text-danger' or '') + ' text-right'"
</strong> />
</td> <div t-if="line.discount">
<td class="text-right"> <t t-esc="(1-line.discount / 100.0) * line.price_unit" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
<div </div>
t-field="line.price_unit" </td>
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}' <td t-if="display_discount">
t-att-style="line.discount and 'text-decoration: line-through' or None" <strong t-if="line.discount" class="text-info text-right">
t-att-class="(line.discount and 'text-danger' or '') + ' text-right'" <t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount
/> </strong>
<div t-if="line.discount"> </td>
<t t-esc="(1-line.discount / 100.0) * line.price_unit" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/> <td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
</div> <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td> </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>
<td class="text-right"> <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 <span
data-id="total_amount" t-esc="current_subtotal"
t-field="sale_order.amount_untaxed" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'
/> />
</td> </td>
</tr> </tr>
<t t-foreach="sale_order.amount_by_group" t-as="amount_by_group"> </t>
<tr style="border-bottom:1px solid #dddddd;"> </t>
<t t-if="amount_by_group[3] == 1 and sale_order.amount_untaxed == amount_by_group[2]"> </tbody>
<td> </table>
<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> <div id="total" class="row" name="total" style="page-break-inside: avoid;">
</td> <div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
<td class="text-right"> <table class="table table-sm">
<span t-esc="amount_by_group[1]" <tr class="border-black" style="border-bottom:1px solid #dddddd;">
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/> <td><strong>Subtotal</strong></td>
</td> <td class="text-right">
</t> <span
<t t-else =""> data-id="total_amount"
<td> t-field="sale_order.amount_untaxed"
<span t-esc="amount_by_group[0]"/> t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'
</td> />
<td class="text-right"> </td>
<span t-esc="amount_by_group[1]" </tr>
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/> <t t-foreach="sale_order.amount_by_group" t-as="amount_by_group">
</td> <tr style="border-bottom:1px solid #dddddd;">
</t> <t t-if="amount_by_group[3] == 1 and sale_order.amount_untaxed == amount_by_group[2]">
</tr> <td>
</t> <span t-esc="amount_by_group[0]"/>
<tr class="border-black"> <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><strong>Total</strong></td> </td>
<td class="text-right"> <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}'/> <span t-esc="amount_by_group[1]"
</td> 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> </tr>
</table> </t>
</div> <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>
</div> </div>
</section> </section>
+2 -2
View File
@@ -54,7 +54,7 @@ class CustomerPortal(CustomerPortal):
return request.redirect('/my') return request.redirect('/my')
if order_sudo.state != 'sent': 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() order_sudo.action_cancel()
message = post.get('decline_message') message = post.get('decline_message')
if message: if message:
@@ -95,7 +95,7 @@ class CustomerPortal(CustomerPortal):
option_sudo.add_option_to_order() 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 # note dbo: website_sale code
@http.route(['/my/orders/<int:order_id>/transaction/'], type='json', auth="public", website=True) @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() self.ensure_one()
if self.state not in ['sale', 'done']: if self.state not in ['sale', 'done']:
auth_param = url_encode(self.partner_id.signup_get_auth_param()[self.partner_id.id]) 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) return super(SaleOrder, self)._get_share_url(redirect, signup_partner, pid)
def get_portal_confirmation_action(self): def get_portal_confirmation_action(self):
@@ -1,25 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <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"> <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']"> <div t-if="doc.sale_order_option_ids and doc.state in ['draft', 'sent']">
<h2> <h4>
<span>Optional Products</span> <span>Optional Products</span>
</h2> </h4>
<table class="table table-sm"> <table class="table table-sm">
<thead> <thead>
<tr> <tr>
<th>Product</th> <th class="text-left">Description</th>
<th>Description</th> <th groups="sale.group_discount_per_so_line" class="text-left"></th>
<th groups="sale.group_discount_per_so_line"></th>
<th class="text-right">Unit Price</th> <th class="text-right">Unit Price</th>
</tr> </tr>
</thead> </thead>
<tbody class="sale_tbody"> <tbody class="sale_tbody">
<tr t-foreach="doc.sale_order_option_ids" t-as="option"> <tr t-foreach="doc.sale_order_option_ids" t-as="option">
<td>
<span t-field="option.product_id.name"/>
</td>
<td> <td>
<span t-field="option.name"/> <span t-field="option.name"/>
</td> </td>
@@ -21,7 +21,7 @@
<xpath expr="//section[@id='details']//t[@t-if='not line.display_type']" position="inside"> <xpath expr="//section[@id='details']//t[@t-if='not line.display_type']" position="inside">
<td class="text-center" t-if="display_remove"> <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> <span class="fa fa-trash-o"></span>
</a> </a>
</td> </td>
@@ -31,16 +31,16 @@
<t t-if="any([(not option.line_id) for option in sale_order.sale_order_option_ids])"> <t t-if="any([(not option.line_id) for option in sale_order.sale_order_option_ids])">
<section> <section>
<h1 class="page-header">Optional Products</h1> <h1 class="page-header">Optional Products</h1>
</section> <t t-set="display_discount" t-value="True in [option.discount for option in sale_order.sale_order_option_ids]"/>
<section>
<div style="overflow-x:auto;">
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th>Product</th> <th class="text-left">Product</th>
<th></th> <th t-if="display_discount" class="text-right">
<span>Discount</span>
</th>
<th class="text-right">Price</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> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -49,8 +49,8 @@
<td> <td>
<div t-field="option.name"/> <div t-field="option.name"/>
</td> </td>
<td> <td t-if="display_discount">
<strong t-if="option.discount" class="text-info"> <strong t-if="option.discount" class="text-info text-right">
<t t-esc="((option.discount % 1) and '%s' or '%d') % option.discount"/>% discount <t t-esc="((option.discount % 1) and '%s' or '%d') % option.discount"/>% discount
</strong> </strong>
</td> </td>
@@ -65,8 +65,8 @@
</div> </div>
</strong> </strong>
</td> </td>
<td class="text-center" t-if="sale_order.state in ['draft', 'sent']"> <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('/add_option/%s' % option.id)" class="mb8 d-print-none" aria-label="Add to cart" title="Add to cart"> <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"/> <span class="fa fa-shopping-cart"/>
</a> </a>
</td> </td>
@@ -74,7 +74,6 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
</section> </section>
</t> </t>
</xpath> </xpath>
@@ -85,7 +84,7 @@
<div class="input-group-prepend d-print-none"> <div class="input-group-prepend d-print-none">
<span class="input-group-text"> <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"/> <span class="fa fa-minus"/>
</a> </a>
</span> </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"/> <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"> <div class="input-group-append d-print-none">
<span class="input-group-text"> <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"/> <span class="fa fa-plus"/>
</a> </a>
</span> </span>
@@ -41,7 +41,7 @@
tbody { tbody {
color: gray('700'); color: gray('700');
tr:nth-child(odd) { tr:nth-child(odd) {
background-color: rgba(184, 171, 180, 0.2); background-color: rgba(220, 205, 216, 0.2);
} }
tr td { tr td {
border-top: none; border-top: none;
@@ -71,8 +71,4 @@
color: #fff; 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 { thead {
color: $o-main-text-color; color: $o-main-text-color;
border-bottom: 1px solid #cacaca; border-bottom: 1px solid #cacaca;
@@ -118,18 +112,6 @@
border-top: none; // Override bootstrap for grouped list views 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 // Values styling
.o_list_number { .o_list_number {
text-align: right; text-align: right;
+2 -4
View File
@@ -52,12 +52,10 @@ body {
.row.o_is_line_section, .row.o_is_line_section,
.table > tbody > tr.o_is_line_section td { .table > tbody > tr.o_is_line_section td {
font-weight: bold; font-weight: bold;
background-color: #EEE; background-color: #DDDDDD;
padding: 8px;
border: 1px solid #BBB;
} }
.row.o_is_line_note, .row.o_is_line_note,
tr.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 --> <!-- External layouts styles -->
<template id="address_layout"> <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"> <t t-if="address">
<div class="row"> <div class="row">
<t t-if="information_block"> <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> <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> </ul>
<div t-field="company.report_footer"/> <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: Page:
<span class="page"/> <span class="page"/>
of 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> <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> </ul>
<div t-field="company.report_footer"/> <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"/> Page: <span class="page"/> / <span class="topage"/>
</div> </div>
</div> </div>
@@ -275,7 +275,7 @@
<h4 class="mt0 mb0 text-uppercase" t-field="company.report_header"/> <h4 class="mt0 mb0 text-uppercase" t-field="company.report_header"/>
</div> </div>
<div class="col-1"> <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> <li class="list-inline-item"><strong><span class="page"/></strong></li>
</ul> </ul>
</div> </div>
@@ -298,9 +298,9 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-6" name="company_address"> <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}' t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'
style="border-bottom: 1px solid black; display:inline-block;"/> />
</div> </div>
</div> </div>
</div> </div>
@@ -323,7 +323,7 @@
<span t-field="company.report_footer"/> <span t-field="company.report_footer"/>
</div> </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"/> Page: <span class="page"/> / <span class="topage"/>
</div> </div>
</div> </div>
@@ -22,12 +22,13 @@
</t> </t>
<div itemprop="address" itemscope="itemscope" itemtype="http://schema.org/PostalAddress"> <div itemprop="address" itemscope="itemscope" itemtype="http://schema.org/PostalAddress">
<div t-if="address and 'address' in fields"> <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>
<div t-if="city and 'city' in fields"> <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"/> <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="addressLocality" t-esc="city"/>,
<span itemprop="addressCountry" t-raw="country_id"/> <span itemprop="addressCountry" t-esc="country_id"/>
</div> </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="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> <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>