diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py index 0fcb9485d76d4ca5c54ecfde482310ce0aa684cf..2cb66869051ef490e47a823e4062a112ecede569 100644 --- a/addons/website_quote/controllers/main.py +++ b/addons/website_quote/controllers/main.py @@ -2,8 +2,10 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import werkzeug +from functools import partial from odoo import fields, http, _ +from odoo.tools import formatLang from odoo.http import request from odoo.addons.website_mail.controllers.main import _message_post_helper @@ -104,8 +106,17 @@ class sale_quote(http.Controller): _message_post_helper(message=message, res_id=order_id, res_model='sale.order', **{'token': token, 'token_field': 'access_token'} if token else {}) return werkzeug.utils.redirect("/quote/%s/%s?message=2" % (order_id, token)) + # Deprecated because override opportunities are **really** limited + # In fact it should be removed in master ASAP @http.route(['/quote/update_line'], type='json', auth="public", website=True) def update(self, line_id, remove=False, unlink=False, order_id=None, token=None, **post): + values = self.update_line_dict(line_id, remove, unlink, order_id, token, **post) + if values: + return [values['order_line_product_uom_qty'], values['order_amount_total']] + return values + + @http.route(['/quote/update_line_dict'], type='json', auth="public", website=True) + def update_line_dict(self, line_id, remove=False, unlink=False, order_id=None, token=None, input_quantity=False, **kwargs): Order = request.env['sale.order'].sudo().browse(int(order_id)) if token != Order.access_token: return request.render('website.404') @@ -115,10 +126,27 @@ class sale_quote(http.Controller): if unlink: OrderLine.unlink() return False - number = -1 if remove else 1 - quantity = OrderLine.product_uom_qty + number + + if input_quantity is not False: + quantity = input_quantity + else: + number = -1 if remove else 1 + quantity = OrderLine.product_uom_qty + number + + if quantity < 0: + quantity = 0.0 OrderLine.write({'product_uom_qty': quantity}) - return [str(quantity), str(Order.amount_total)] + currency = Order.currency_id + format_price = partial(formatLang, request.env, digits=currency.decimal_places) + + return { + 'order_line_product_uom_qty': str(quantity), + 'order_line_price_total': format_price(OrderLine.price_total), + 'order_line_price_subtotal': format_price(OrderLine.price_subtotal), + 'order_amount_total': format_price(Order.amount_total), + 'order_amount_untaxed': format_price(Order.amount_untaxed), + 'order_amount_tax': format_price(Order.amount_tax), + } @http.route(["/quote/template/<model('sale.quote.template'):quote>"], type='http', auth="user", website=True) def template_view(self, quote, **post): diff --git a/addons/website_quote/static/src/js/website_quotation.js b/addons/website_quote/static/src/js/website_quotation.js index 04705686f32563fcec1c1ba5ecf17af7bdd1e2a8..6985625c378ee92f8e7119bed3c24f1ea84be55d 100644 --- a/addons/website_quote/static/src/js/website_quotation.js +++ b/addons/website_quote/static/src/js/website_quotation.js @@ -15,28 +15,160 @@ if(!$('.o_website_quote').length) { events: { 'click' : 'onClick', }, - onClick: function(ev){ - ev.preventDefault(); + /** + * @override + */ + start: function () { var self = this; - var href = this.$el.attr("href"); - var order_id = href.match(/order_id=([0-9]+)/); - var line_id = href.match(/update_line\/([0-9]+)/); - var token = href.match(/token=(.*)/); - ajax.jsonRpc("/quote/update_line", 'call', { - 'line_id': line_id[1], - 'order_id': parseInt(order_id[1]), - 'token': token[1], - 'remove': self.$el.is('[href*="remove"]'), - 'unlink': self.$el.is('[href*="unlink"]') - }).then(function (data) { - if(!data){ - location.reload(); - } - self.$el.parents('.input-group:first').find('.js_quantity').val(data[0]); - $('[data-id="total_amount"]>span').html(data[1]); + return this._super.apply(this, arguments).then(function () { + self.elems = self._getUpdatableElements(); + self.elems.$lineQuantity.change(function (ev) { + var quantity = parseInt(this.value); + self._onChangeQuantity(quantity); + }); }); + }, + /** + * Process the change in line quantity + * + * @private + * @param {Int} quantity, the new quantity of the line + * If not present it will increment/decrement the existing quantity + */ + _onChangeQuantity: function (quantity) { + var href = this.$el.attr("href"); + var order_id = href.match(/order_id=([0-9]+)/)[1]; + var line_id = href.match(/update_line(_dict)?\/([0-9]+)/)[2]; + var token = href.match(/token=([\w\d-]*)/)[1]; + + var callParams = { + 'line_id': parseInt(line_id), + 'order_id': parseInt(order_id), + 'token': token, + 'remove': this.$el.is('[href*="remove"]'), + 'unlink': this.$el.is('[href*="unlink"]'), + 'input_quantity': quantity >= 0 ? quantity : false, + }; + this._callUpdateLineRoute(callParams).then(this._updateOrderValues.bind(this)); return false; }, + /** + * Reacts to the click on the -/+ buttons + * + * @param {Event} ev + */ + onClick: function (ev) { + ev.preventDefault(); + return this._onChangeQuantity(); + }, + /** + * Calls the route to get updated values of the line and order + * when the quantity of a product has changed + * + * @private + * @param {Object} params + * @return {Deferred} + */ + _callUpdateLineRoute: function (params) { + var def = new $.Deferred(); + ajax.jsonRpc("/quote/update_line_dict", 'call', params) + .then(def.resolve.bind(def)) + .fail(function () { + // Compatibility: the server may not have been restarted + // So the real route may not exist + delete params.input_quantity; + ajax.jsonRpc("/quote/update_line", 'call', params) + .fail(def.reject.bind(def)) + .then(function (data) { + // Data is an array, convert it to a dict + var actualData = data; + if (data) { + actualData = { + order_amount_total: data[1], + order_line_product_uom_qty: data[0], + }; + } + def.resolve(actualData); + }); + }); + return def; + }, + /** + * Processes data from the server to update the UI + * + * @private + * @param {Object} data: contains order and line updated values + */ + _updateOrderValues: function (data) { + if (!data) { + window.location.reload(); + } + + var orderAmountTotal = data.order_amount_total; + var orderAmountUntaxed = data.order_amount_untaxed; + var orderAmountTax = data.order_amount_tax; + + var lineProductUomQty = data.order_line_product_uom_qty; + var linePriceTotal = data.order_line_price_total; + var linePriceSubTotal = data.order_line_price_subtotal; + + this.elems.$lineQuantity.val(lineProductUomQty) + + if (this.elems.$linePriceTotal.length && linePriceTotal !== undefined) { + this.elems.$linePriceTotal.text(linePriceTotal); + } + if (this.elems.$linePriceSubTotal.length && linePriceSubTotal !== undefined) { + this.elems.$linePriceSubTotal.text(linePriceSubTotal); + } + + if (orderAmountUntaxed !== undefined) { + this.elems.$orderAmountUntaxed.text(orderAmountUntaxed); + } + + if (orderAmountTax !== undefined) { + this.elems.$orderAmountTax.text(orderAmountTax); + } + + if (orderAmountTotal !== undefined) { + this.elems.$orderAmountTotal.text(orderAmountTotal); + } + }, + /** + * 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 + * + * @private + * @return {Object}: Jquery elements to update + */ + _getUpdatableElements: function () { + var $parentTr = this.$el.parents('tr:first'); + var $linePriceTotal = $parentTr.find('.oe_order_line_price_total .oe_currency_value'); + var $linePriceSubTotal = $parentTr.find('.oe_order_line_price_subtotal .oe_currency_value'); + + if (!$linePriceTotal.length && !$linePriceSubTotal.length) { + $linePriceTotal = $linePriceSubTotal = $parentTr.find('.oe_currency_value').last(); + } + + var $orderAmountUntaxed = $('[data-id="total_untaxed"]>span'); + var $orderAmountTax = $('[data-id="total_taxes"]>span'); + var $orderAmountTotal = $('[data-id="total_amount"]>span'); + + if (!$orderAmountUntaxed.length && !$orderAmountTax.length) { + $orderAmountUntaxed = $orderAmountTotal.eq(1); + $orderAmountTax = $orderAmountTotal.eq(2); + $orderAmountTotal = $orderAmountTotal.eq(0).add($orderAmountTotal.eq(3)); + } + + return { + $lineQuantity: this.$el.parents('.input-group:first').find('.js_quantity'), + $linePriceSubTotal: $linePriceSubTotal, + $linePriceTotal: $linePriceTotal, + $orderAmountUntaxed: $orderAmountUntaxed, + $orderAmountTax: $orderAmountTax, + $orderAmountTotal: $orderAmountTotal, + } + } }); var update_button_list = []; diff --git a/addons/website_quote/views/website_quote_templates.xml b/addons/website_quote/views/website_quote_templates.xml index 2aa63f2f7308fc1db5858feee4b7b4de72e39fe4..1b4c906a5004d31d72fa9261ede95ef78f534459 100644 --- a/addons/website_quote/views/website_quote_templates.xml +++ b/addons/website_quote/views/website_quote_templates.xml @@ -69,11 +69,11 @@ t-options="{'widget': 'monetary', 'display_currency': quotation.pricelist_id.currency_id}"/> </div> </td> - <td class="text-right" groups="sale.group_show_price_subtotal"> + <td class="text-right oe_order_line_price_subtotal" groups="sale.group_show_price_subtotal"> <span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/> </td> - <td class="text-right" groups="sale.group_show_price_total"> + <td class="text-right oe_order_line_price_total" groups="sale.group_show_price_total"> <span t-field="line.price_total" t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/> </td> @@ -102,7 +102,7 @@ <td></td><td></td><td></td><td></td> <td class="text-right"><strong>Subtotal:</strong></td> <td class="text-right"> - <strong data-id="total_amount" t-field="quotation.amount_untaxed" t-options='{"widget": "monetary","display_currency": quotation.pricelist_id.currency_id}'/> + <strong data-id="total_untaxed" t-field="quotation.amount_untaxed" t-options='{"widget": "monetary","display_currency": quotation.pricelist_id.currency_id}'/> </td> <td></td> </tr> @@ -111,7 +111,7 @@ <td></td><td></td><td></td><td></td> <td class="text-right">Taxes:</td> <td class="text-right"> - <span data-id="total_amount" t-field="quotation.amount_tax" t-options='{"widget": "monetary","display_currency": quotation.pricelist_id.currency_id}'/> + <span data-id="total_taxes" t-field="quotation.amount_tax" t-options='{"widget": "monetary","display_currency": quotation.pricelist_id.currency_id}'/> </td> <td></td> </tr>