diff --git a/addons/sale_management/controllers/portal.py b/addons/sale_management/controllers/portal.py index 99deb40075b..c3763df6a84 100644 --- a/addons/sale_management/controllers/portal.py +++ b/addons/sale_management/controllers/portal.py @@ -1,41 +1,43 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from functools import partial - -from odoo import http -from odoo.tools import formatLang from odoo.exceptions import AccessError, MissingError -from odoo.http import request +from odoo.http import request, route + from odoo.addons.sale.controllers import portal class CustomerPortal(portal.CustomerPortal): - def _get_portal_order_details(self, order_sudo, order_line=False): - currency = order_sudo.currency_id - format_price = partial(formatLang, request.env, digits=currency.decimal_places) - results = { - 'order_amount_total': format_price(order_sudo.amount_total), - 'order_amount_untaxed': format_price(order_sudo.amount_untaxed), - 'order_amount_tax': format_price(order_sudo.amount_tax), - 'order_amount_undiscounted': format_price(order_sudo.amount_undiscounted), + def _get_order_portal_content(self, order_sudo): + """ Return the order portal details. + + :return: rendered html of the order portal details + :rtype: dict + """ + return { + 'sale_template': request.env['ir.ui.view']._render_template( + 'sale.sale_order_portal_content', { + 'sale_order': order_sudo, + 'report_type': 'html', + }, + ), } - if order_line: - results.update({ - 'order_line_product_uom_qty': str(order_line.product_uom_qty), - 'order_line_price_total': format_price(order_line.price_total), - 'order_line_price_subtotal': format_price(order_line.price_subtotal) - }) - try: - results['order_totals_table'] = request.env['ir.ui.view']._render_template('sale.sale_order_portal_content_totals_table', {'sale_order': order_sudo}) - except ValueError: - pass - return results + @route(['/my/orders//update_line_dict'], type='json', auth="public", website=True) + def portal_quote_option_update(self, order_id, line_id, access_token=None, remove=False, unlink=False, input_quantity=False, **kwargs): + """ Update the quantity or Remove an optional SOline from a SO. - @http.route(['/my/orders//update_line_dict'], type='json', auth="public", website=True) - def update_line_dict(self, line_id, remove=False, unlink=False, order_id=None, access_token=None, input_quantity=False, **kwargs): + :param int order_id: `sale.order` id + :param int line_id: `sale.order.line` id + :param str access_token: portal access_token of the specified order + :param bool remove: if true, 1 unit will be removed from the line + :param bool unlink: if true, the option will be removed from the SO + :param float input_quantity: if specified, will be set as new line qty + :param dict kwargs: unused parameters + :return: New order details (as html content) + :rtype: dict + """ try: order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token) except (AccessError, MissingError): @@ -55,23 +57,22 @@ class CustomerPortal(portal.CustomerPortal): if unlink or quantity <= 0: order_line.unlink() - results = self._get_portal_order_details(order_sudo) - results.update({ - 'unlink': True, - 'sale_template': request.env['ir.ui.view']._render_template('sale.sale_order_portal_content', { - 'sale_order': order_sudo, - 'report_type': "html" - }), - }) - return results + else: + order_line.product_uom_qty = quantity - order_line.write({'product_uom_qty': quantity}) - results = self._get_portal_order_details(order_sudo, order_line) + return self._get_order_portal_content(order_sudo) - return results + @route(["/my/orders//add_option/"], type='json', auth="public", website=True) + def portal_quote_add_option(self, order_id, option_id, access_token=None, **kwargs): + """ Add the specified option to the specified order. - @http.route(["/my/orders//add_option/"], type='json', auth="public", website=True) - def add(self, order_id, option_id, access_token=None, **post): + :param int order_id: `sale.order` id + :param int option_id: `sale.order.option` id + :param str access_token: portal access_token of the specified order + :param dict kwargs: unused parameters + :return: New order details (as html content) + :rtype: dict + """ try: order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token) except (AccessError, MissingError): @@ -83,9 +84,4 @@ class CustomerPortal(portal.CustomerPortal): return request.redirect(order_sudo.get_portal_url()) option_sudo.add_option_to_order() - results = self._get_portal_order_details(order_sudo) - results['sale_template'] = request.env['ir.ui.view']._render_template("sale.sale_order_portal_content", { - 'sale_order': option_sudo.order_id, - 'report_type': "html" - }) - return results + return self._get_order_portal_content(order_sudo) diff --git a/addons/sale_management/static/src/js/sale_management.js b/addons/sale_management/static/src/js/sale_management.js index eaa126ccabf..72f57913336 100644 --- a/addons/sale_management/static/src/js/sale_management.js +++ b/addons/sale_management/static/src/js/sale_management.js @@ -6,87 +6,19 @@ var publicWidget = require('web.public.widget'); publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({ selector: '.o_portal_sale_sidebar', events: { - 'click a.js_update_line_json': '_onClick', - 'click a.js_add_optional_products': '_onClickOptionalProduct', - 'change .js_quantity': '_onChangeQuantity' + 'click a.js_update_line_json': '_onClickOptionQuantityButton', + 'click a.js_add_optional_products': '_onClickAddOptionalProduct', + 'change .js_quantity': '_onChangeOptionQuantity', }, + /** * @override */ async start() { await this._super(...arguments); this.orderDetail = this.$el.find('table#sales_order_table').data(); - this.elems = this._getUpdatableElements(); }, - /** - * Process the change in line quantity - * - * @private - * @param {Event} ev - */ - _onChangeQuantity(ev) { - ev.preventDefault(); - let self = this, - $target = $(ev.currentTarget), - quantity = parseInt($target.val()); - this._callUpdateLineRoute(self.orderDetail.orderId, { - 'line_id': $target.data('lineId'), - 'input_quantity': quantity >= 0 ? quantity : false, - 'access_token': self.orderDetail.token - }).then((data) => { - self._updateOrderLineValues($target.closest('tr'), data); - self._updateOrderValues(data); - }); - }, - /** - * Reacts to the click on the -/+ buttons - * - * @param {Event} ev - */ - _onClick(ev) { - ev.preventDefault(); - let self = this, - $target = $(ev.currentTarget); - this._callUpdateLineRoute(self.orderDetail.orderId, { - 'line_id': $target.data('lineId'), - 'remove': $target.data('remove'), - 'unlink': $target.data('unlink'), - 'access_token': self.orderDetail.token - }).then((data) => { - var $saleTemplate = $(data['sale_template']); - if ($saleTemplate.length && data['unlink']) { - self.$('#portal_sale_content').html($saleTemplate); - self.elems = self._getUpdatableElements(); - } - self._updateOrderLineValues($target.closest('tr'), data); - self._updateOrderValues(data); - }); - }, - /** - * trigger when optional product added to order from portal. - * - * @private - * @param {Event} ev - */ - _onClickOptionalProduct(ev) { - ev.preventDefault(); - let self = this, - $target = $(ev.currentTarget); - // to avoid double click on link with href. - $target.css('pointer-events', 'none'); - - this._rpc({ - route: "/my/orders/" + self.orderDetail.orderId + "/add_option/" + $target.data('optionId'), - params: {access_token: self.orderDetail.token} - }).then((data) => { - if (data) { - self.$('#portal_sale_content').html($(data['sale_template'])); - self.elems = self._getUpdatableElements(); - self._updateOrderValues(data); - } - }); - }, /** * Calls the route to get updated values of the line and order * when the quantity of a product has changed @@ -96,87 +28,87 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({ * @param {Object} params * @return {Deferred} */ - _callUpdateLineRoute(order_id, params) { + _callUpdateLineRoute(order_id, params) { return this._rpc({ route: "/my/orders/" + order_id + "/update_line_dict", params: params, }); }, + /** - * Processes data from the server to update the orderline UI + * Refresh the UI of the order details * * @private - * @param {Element} $orderLine: orderline element to update - * @param {Object} data: contains order and line updated values + * @param {Object} data: contains order html details */ - _updateOrderLineValues($orderLine, data) { - let linePriceTotal = data.order_line_price_total, - linePriceSubTotal = data.order_line_price_subtotal, - $linePriceTotal = $orderLine.find('.oe_order_line_price_total .oe_currency_value'), - $linePriceSubTotal = $orderLine.find('.oe_order_line_price_subtotal .oe_currency_value'); - - if (!$linePriceTotal.length && !$linePriceSubTotal.length) { - $linePriceTotal = $linePriceSubTotal = $orderLine.find('.oe_currency_value').last(); - } - - $orderLine.find('.js_quantity').val(data.order_line_product_uom_qty); - if ($linePriceTotal.length && linePriceTotal !== undefined) { - $linePriceTotal.text(linePriceTotal); - } - if ($linePriceSubTotal.length && linePriceSubTotal !== undefined) { - $linePriceSubTotal.text(linePriceSubTotal); + _refreshOrderUI(data){ + const $saleTemplate = $(data['sale_template']); + if ($saleTemplate.length) { + this.$('#portal_sale_content').html($saleTemplate); } }, + /** - * Processes data from the server to update the UI + * Process the change in line quantity * * @private - * @param {Object} data: contains order and line updated values + * @param {Event} ev */ - _updateOrderValues(data) { - let orderAmountTotal = data.order_amount_total, - orderAmountUntaxed = data.order_amount_untaxed, - orderAmountUndiscounted = data.order_amount_undiscounted, - $orderTotalsTable = $(data.order_totals_table); - if (orderAmountUntaxed !== undefined) { - this.elems.$orderAmountUntaxed.text(orderAmountUntaxed); - } + async _onChangeOptionQuantity(ev) { + ev.preventDefault(); + let self = this, + $target = $(ev.currentTarget), + quantity = parseInt($target.val()); - if (orderAmountTotal !== undefined) { - this.elems.$orderAmountTotal.text(orderAmountTotal); - } - - if (orderAmountUndiscounted !== undefined) { - this.elems.$orderAmountUndiscounted.text(orderAmountUndiscounted); - } - if ($orderTotalsTable.length) { - this.elems.$orderTotalsTable.find('table').replaceWith($orderTotalsTable); - } + const result = await this._callUpdateLineRoute(self.orderDetail.orderId, { + 'line_id': $target.data('lineId'), + 'input_quantity': quantity >= 0 ? quantity : false, + 'access_token': self.orderDetail.token + }); + this._refreshOrderUI(result); }, + /** - * Locate in the DOM the elements to update - * Mostly for compatibility, when the module has not been upgraded - * In that case, we need to fall back to some other elements + * Reacts to the click on the -/+ buttons * * @private - * @return {Object}: Jquery elements to update + * @param {Event} ev */ - _getUpdatableElements() { - let $orderAmountUntaxed = $('[data-id="total_untaxed"]').find('span, b'), - $orderAmountTotal = $('[data-id="total_amount"]').find('span, b'), - $orderAmountUndiscounted = $('[data-id="amount_undiscounted"]').find('span, b'); + async _onClickOptionQuantityButton(ev) { + ev.preventDefault(); + let self = this, + $target = $(ev.currentTarget); - if (!$orderAmountUntaxed.length) { - $orderAmountUntaxed = $orderAmountTotal.eq(1); - $orderAmountTotal = $orderAmountTotal.eq(0).add($orderAmountTotal.eq(2)); - } + const result = await this._callUpdateLineRoute(self.orderDetail.orderId, { + 'line_id': $target.data('lineId'), + 'remove': $target.data('remove'), + 'unlink': $target.data('unlink'), + 'access_token': self.orderDetail.token + }); + this._refreshOrderUI(result); + }, + + /** + * Triggered when optional product added to order from portal. + * + * @private + * @param {Event} ev + */ + _onClickAddOptionalProduct(ev) { + ev.preventDefault(); + let self = this, + $target = $(ev.currentTarget); + + // to avoid double click on link with href. + $target.css('pointer-events', 'none'); + + this._rpc({ + route: "/my/orders/" + self.orderDetail.orderId + "/add_option/" + $target.data('optionId'), + params: {access_token: self.orderDetail.token} + }).then((data) => { + this._refreshOrderUI(data); + }); + }, - return { - $orderAmountUntaxed: $orderAmountUntaxed, - $orderAmountTotal: $orderAmountTotal, - $orderTotalsTable: $('#total'), - $orderAmountUndiscounted: $orderAmountUndiscounted, - }; - } }); });