From 949ce0278a99226ec51f04008571b7410632ea3c Mon Sep 17 00:00:00 2001 From: jpr-odoo Date: Mon, 19 Mar 2018 15:39:52 +0530 Subject: [PATCH 1/8] [IMP] portal: add a generic PortalSidebar widget to be used in customer portal with this commit, this generic widget allow to basic functionality for portal sidebar like: print btn (direclty print the document), set due or delay label of the document (based on given date), used affix to locked to an area on the page for sidebar. Purpose is to reuse it in account customer portal and online quotes notably. some css code convert to the less file to have working portal sidebar skeleton, which is already used in website_quote Task ID: 39876 --- addons/portal/static/src/js/portal_sidebar.js | 107 ++++++++++++++++++ addons/portal/static/src/scss/portal.scss | 27 +++++ addons/portal/views/assets.xml | 1 + addons/portal/views/portal_templates.xml | 14 ++- 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 addons/portal/static/src/js/portal_sidebar.js diff --git a/addons/portal/static/src/js/portal_sidebar.js b/addons/portal/static/src/js/portal_sidebar.js new file mode 100644 index 00000000000..aebb601e911 --- /dev/null +++ b/addons/portal/static/src/js/portal_sidebar.js @@ -0,0 +1,107 @@ +odoo.define('portal.PortalSidebar', function (require) { +"use strict"; + +require('web.dom_ready'); +var config = require('web.config'); +var core = require('web.core'); +var Widget = require('web.Widget'); +var time = require('web.time'); + +var _t = core._t; + +var PortalSidebar = Widget.extend({ + /** + * @override + */ + start: function () { + var self = this; + this._super.apply(this, arguments); + // Window Handlers + $(window).on('resize', _.throttle(self._onUpdateSidebarPosition.bind(self), 200, {leading: false})); + this._onUpdateSidebarPosition(); + this._setAffix(); + this._setDelayLabel(); + }, + + //-------------------------------------------------------------------------- + // Private + //--------------------------------------------------------------------------- + + /** + * Set the due/delay information according to the given date + * like : + * @private + */ + _setDelayLabel : function () { + var $sidebarTimeago = this.$el.find('.o_portal_sidebar_timeago'); + _.each($sidebarTimeago, function (el) { + var dateTime = moment(time.auto_str_to_date($(el).attr('datetime'))), + today = moment().startOf('day'), + diff = dateTime.diff(today, 'days', true), + displayStr; + + if (diff === 0){ + displayStr = _t('Due today'); + } else if (diff > 0) { + displayStr = _.str.sprintf(_t('Due in %d days'), Math.abs(diff)); + } else { + displayStr = _.str.sprintf(_t('%d days overdue'), Math.abs(diff)); + } + $(el).text(displayStr); + }); + }, + /** + * updates affix status of sidebar for window scrolle + * + * @private + */ + _setAffix : function () { + var $bsSidebar = this.$el.find('.bs-sidebar'); + $bsSidebar.affix({ + offset: { + top: 0, + bottom: $('#wrapwrap').outerHeight() - $('main').height(), + }, + }); + }, + /** + * @private + * @param {string} href + */ + _printIframeContent: function (href) { + // due to this issue : https://bugzilla.mozilla.org/show_bug.cgi?id=911444 + // open a new window with pdf for print in Firefox (in other system: http://printjs.crabbly.com) + if ($.browser.mozilla) { + window.open(href, '_blank'); + return ; + } + if (!this.printContent) { + this.printContent = $(''); + this.$el.append(this.printContent); + this.printContent.on('load', function () { + $(this).get(0).contentWindow.print(); + }); + } else { + this.printContent.get(0).contentWindow.print(); + } + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Called when the window is resized or on loadoing widget + * + * @private + */ + _onUpdateSidebarPosition: function () { + var $sidebar = this.$el.find('.bs-sidebar, .o_portal_brand'); + $sidebar.css({ + position: config.device.size_class >= config.device.SIZES.SM ? "fixed" : '', + width: config.device.size_class >= config.device.SIZES.SM ? $sidebar.outerWidth() : '', + }); + }, +}); +return PortalSidebar; +}); diff --git a/addons/portal/static/src/scss/portal.scss b/addons/portal/static/src/scss/portal.scss index b49f50189c6..9eb2c5046b7 100644 --- a/addons/portal/static/src/scss/portal.scss +++ b/addons/portal/static/src/scss/portal.scss @@ -392,3 +392,30 @@ img.o_we_custom_image { .label-optional { font-weight: normal; } + +.o_portal_sidebar { + .bs-sidebar { + position: relative; + width: 100%; + border-radius: 5px; + background-color: $navbar-default-bg; + } + .o_print_btn { + width: 52%; + } + .o_portal_contact_img { + width: 3.7em; + height: 3.7em; + } + .o_portal_html_view { + border: 1px solid $gray-lighter-dark; + border-radius: 5px; + padding-right: 1px; + } + // full width of download button for better usability in mobile device + @media (max-width: $screen-xs-max), (max-width: $screen-sm-max) { + .o_download_btn { + width: 100%; + } + } +} diff --git a/addons/portal/views/assets.xml b/addons/portal/views/assets.xml index 44af4db1950..15a3de3d775 100644 --- a/addons/portal/views/assets.xml +++ b/addons/portal/views/assets.xml @@ -10,6 +10,7 @@ + diff --git a/addons/portal/views/portal_templates.xml b/addons/portal/views/portal_templates.xml index 9522a1fcda4..ad18e847f7b 100644 --- a/addons/portal/views/portal_templates.xml +++ b/addons/portal/views/portal_templates.xml @@ -57,7 +57,10 @@
-
@@ -412,4 +415,13 @@ t-att-data-sign-label="signLabel" t-att-data-access-token="accessToken"/> + + From cea2a454ae20737b95f802179cac5c01e17e0bdf Mon Sep 17 00:00:00 2001 From: jpr-odoo Date: Mon, 19 Mar 2018 16:08:14 +0530 Subject: [PATCH 2/8] [IMP] account*: update invoice page view on customer portal *account_payment, l10_in(_sale), sale(_stock), web With this commit: The invoice preview in portal (/my/invoices..) is now the HTML version of the PDF report. The HTML preview is based on the invoice report to closely match what is already done in Odoo. The invoice preview is displayed in an 'iframe', it is mandatory since: 1. Reports are rendered as a separate page as it returns the whole page after rendering, which has tag , etc 2. Reports are having their own layouts and their own assets that we we cannot load in the page directly or it will break the website layout. 3. We can not use report template with t-call because internally reports are using extra params like context_timestamp, company, DateTime, user etc which is used in header/footer -- There will be a chatter (as a modal) to display/see the history of the document -- For responsive purpose, we use new tables with limited column (Description, Quantity and Amount) for responsive purpose (small screen). It also adds some bootstrap classes to reports for responsive purpose. -- The 'Pay Now' button remains if you already paid with 'wire transfer'. -- Add margin after the header and before the footer to look like the real printed document, without alterating the real printed version Task ID: 39876 --- addons/account/controllers/portal.py | 30 ++- .../account/report/account_invoice_report.py | 14 + .../static/src/js/account_portal_sidebar.js | 58 +++++ addons/account/views/account.xml | 6 + .../views/account_portal_templates.xml | 241 +++++------------- addons/account/views/report_invoice.xml | 54 +++- .../views/account_portal_templates.xml | 145 ++++++----- addons/l10n_in/views/report_invoice.xml | 2 +- .../views/report_invoice_view.xml | 2 +- .../sale/report/invoice_report_templates.xml | 69 ++++- .../views/account_invoice_views.xml | 3 +- .../static/src/scss/layout_background.scss | 2 +- addons/web/static/src/scss/layout_boxed.scss | 1 + addons/web/static/src/scss/layout_clean.scss | 2 + .../web/static/src/scss/layout_standard.scss | 3 + addons/web/views/report_templates.xml | 5 +- 16 files changed, 382 insertions(+), 255 deletions(-) create mode 100644 addons/account/static/src/js/account_portal_sidebar.js create mode 100644 addons/web/static/src/scss/layout_standard.scss diff --git a/addons/account/controllers/portal.py b/addons/account/controllers/portal.py index 155c38d1a05..f78f8d45935 100644 --- a/addons/account/controllers/portal.py +++ b/addons/account/controllers/portal.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import re +from werkzeug.exceptions import NotFound + from odoo import http, _ from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager from odoo.exceptions import AccessError @@ -113,9 +116,14 @@ class PortalAccount(CustomerPortal): return request.redirect('/my') values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw) + # force to hide orginal breadcrumbs if we have access_token or not + values['no_breadcrumbs'] = True return request.render("account.portal_invoice_page", values) - @http.route(['/my/invoices/pdf/'], type='http', auth="public", website=True) + @http.route([ + '/my/invoices/pdf/', + '/my/invoices/html//' + ], type='http', auth="public", website=True) def portal_my_invoice_report(self, invoice_id, access_token=None, **kw): try: invoice_sudo = self._invoice_check_access(invoice_id, access_token) @@ -124,12 +132,20 @@ class PortalAccount(CustomerPortal): # print report as sudo, since it require access to taxes, payment term, ... and portal # does not have those access rights. - pdf = request.env.ref('account.account_invoices').sudo().render_qweb_pdf([invoice_sudo.id])[0] - pdfhttpheaders = [ - ('Content-Type', 'application/pdf'), - ('Content-Length', len(pdf)), - ] - return request.make_response(pdf, headers=pdfhttpheaders) + 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 diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index 791d0820f63..0282010032e 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -171,3 +171,17 @@ class AccountInvoiceReport(models.Model): )""" % ( self._table, self.env['res.currency']._select_companies_rates(), self._select(), self._sub_select(), self._from(), self._group_by())) + + +class ReportInvoiceWithPayment(models.AbstractModel): + _name = 'report.account.report_invoice_with_payments' + + @api.model + def get_report_values(self, docids, data=None): + report = self.env['ir.actions.report']._get_report_from_name('account.report_invoice_with_payments') + return { + 'doc_ids': docids, + 'doc_model': report.model, + 'docs': self.env[report.model].browse(docids), + 'type_html': data and data.get('report_type') == 'html', + } diff --git a/addons/account/static/src/js/account_portal_sidebar.js b/addons/account/static/src/js/account_portal_sidebar.js new file mode 100644 index 00000000000..3b0ab0e6afe --- /dev/null +++ b/addons/account/static/src/js/account_portal_sidebar.js @@ -0,0 +1,58 @@ +odoo.define('account.AccountPortalSidebar', function (require) { +"use strict"; + +require('web.dom_ready'); +var PortalSidebar = require('portal.PortalSidebar'); + +var AccountPortalSidebar = PortalSidebar.extend({ + events: { + 'click .o_portal_invoice_print': '_onPrintInvoice', + }, + /** + * @override + */ + start: function () { + var self = this; + this._super.apply(this, arguments); + var $invoiceHtml = this.$el.find('iframe#invoice_html'); + $invoiceHtml.on('load', self._onLoadIframe.bind(self, $invoiceHtml)); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Called when the iframe is load on custome portal + * here we set height and width of html preview (iframe) + * + * @private + */ + _onLoadIframe: function ($el) { + var $body = $el.contents().find('body'); + // expand iframe height for display full report on the custome portal (no scrollbar in iframe) + $el.height($body.scrollParent().height()); + // removed extra space in html preview (specially left and right margin) + $body.width('100%'); + }, + /** + * @private + * @param {MouseEvent} ev + */ + _onPrintInvoice: function (ev) { + ev.preventDefault(); + var href = $(ev.currentTarget).attr('href'); + this._printIframeContent(href); + }, +}); + +$(function () { + if (!$('.o_portal_invoice_sidebar').length) { + return $.Deferred().reject("DOM doesn't contain '.o_portal_invoice_sidebar'"); + } + var account_portal_sidebar = new AccountPortalSidebar(); + account_portal_sidebar.attachTo($('.o_portal_invoice_sidebar')); +}); + +return AccountPortalSidebar; +}); diff --git a/addons/account/views/account.xml b/addons/account/views/account.xml index d8153c82871..e7218152387 100644 --- a/addons/account/views/account.xml +++ b/addons/account/views/account.xml @@ -24,6 +24,12 @@ + + -