[IMP] sale, sale_management: avoid reload when add optional product added in portal
porpose of the task is to avoid the reload of the web client when you add an optional product, in the customer portal this commit will fix the issue of window reload when add optional product and delete product in customer portal. removed order line from sale order, when a customer set the quantity of an option line to 0 in the portal task-1961015 Closes #32551 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com> Co-authored-by: mba-odoo <mba@odoo.com>
This commit is contained in:
committed by
jpr-odoo
co-authored by
mba-odoo
parent
0b88e862a8
commit
0beebad0ff
@@ -310,7 +310,7 @@
|
||||
</div>
|
||||
|
||||
<!-- main content -->
|
||||
<div t-attf-class="card #{'pb-5' if report_type == 'html' else ''}">
|
||||
<div t-attf-class="card #{'pb-5' if report_type == 'html' else ''}" id="portal_sale_content">
|
||||
<div t-call="sale.sale_order_portal_content"/>
|
||||
</div>
|
||||
|
||||
@@ -428,7 +428,7 @@
|
||||
|
||||
<t t-set="display_discount" t-value="True in [line.discount > 0 for line in sale_order.order_line]"/>
|
||||
|
||||
<table class="table table-sm" id="sales_order_table">
|
||||
<table t-att-data-order-id="sale_order.id" t-att-data-token="sale_order.access_token" class="table table-sm" id="sales_order_table">
|
||||
<thead class="bg-100">
|
||||
<tr>
|
||||
<th class="text-left">Products</th>
|
||||
@@ -519,48 +519,7 @@
|
||||
|
||||
<div id="total" class="row" name="total" style="page-break-inside: avoid;">
|
||||
<div t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto">
|
||||
<!-- Should be replaced in master by t-call="sale.sale_order_portal_content_totals_table" -->
|
||||
<table class="table table-sm">
|
||||
<tr class="border-black" style="border-bottom:1px solid #dddddd;">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<span
|
||||
data-id="total_untaxed"
|
||||
t-field="sale_order.amount_untaxed"
|
||||
t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<t t-foreach="sale_order.amount_by_group" t-as="amount_by_group">
|
||||
<tr style="border-bottom:1px solid #dddddd;">
|
||||
<t t-if="amount_by_group[3] == 1 and sale_order.amount_untaxed == amount_by_group[2]">
|
||||
<td>
|
||||
<span t-esc="amount_by_group[0]"/>
|
||||
<span>&nbsp;<span>on</span>&nbsp;<t t-esc="amount_by_group[2]" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/></span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="amount_by_group[1]"
|
||||
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else ="">
|
||||
<td>
|
||||
<span t-esc="amount_by_group[0]"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="amount_by_group[1]"
|
||||
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</t>
|
||||
<tr class="border-black">
|
||||
<td><strong>Total</strong></td>
|
||||
<td class="text-right">
|
||||
<span data-id="total_amount" t-field="sale_order.amount_total" t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<t t-call="sale.sale_order_portal_content_totals_table"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -12,12 +12,27 @@ from odoo.addons.sale.controllers.portal import CustomerPortal
|
||||
|
||||
class CustomerPortal(CustomerPortal):
|
||||
|
||||
@http.route(['/my/orders/<int:order_id>/update_line'], type='json', auth="public", website=True)
|
||||
def update(self, line_id, remove=False, unlink=False, order_id=None, access_token=None, **post):
|
||||
values = self.update_line_dict(line_id, remove, unlink, order_id, access_token, **post)
|
||||
if values:
|
||||
return [values['order_line_product_uom_qty'], values['order_amount_total']]
|
||||
return values
|
||||
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),
|
||||
}
|
||||
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
|
||||
|
||||
@http.route(['/my/orders/<int:order_id>/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):
|
||||
@@ -31,9 +46,6 @@ class CustomerPortal(CustomerPortal):
|
||||
order_line = request.env['sale.order.line'].sudo().browse(int(line_id))
|
||||
if order_line.order_id != order_sudo:
|
||||
return False
|
||||
if unlink:
|
||||
order_line.unlink()
|
||||
return False # return False to reload the page, the line must move back to options and the JS doesn't handle it
|
||||
|
||||
if input_quantity is not False:
|
||||
quantity = input_quantity
|
||||
@@ -41,29 +53,24 @@ class CustomerPortal(CustomerPortal):
|
||||
number = -1 if remove else 1
|
||||
quantity = order_line.product_uom_qty + number
|
||||
|
||||
if quantity < 0:
|
||||
quantity = 0.0
|
||||
order_line.write({'product_uom_qty': quantity})
|
||||
currency = order_sudo.currency_id
|
||||
format_price = partial(formatLang, request.env, digits=currency.decimal_places)
|
||||
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
|
||||
|
||||
results = {
|
||||
'order_line_product_uom_qty': str(quantity),
|
||||
'order_line_price_total': format_price(order_line.price_total),
|
||||
'order_line_price_subtotal': format_price(order_line.price_subtotal),
|
||||
'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),
|
||||
}
|
||||
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
|
||||
order_line.write({'product_uom_qty': quantity})
|
||||
results = self._get_portal_order_details(order_sudo, order_line)
|
||||
|
||||
return results
|
||||
|
||||
@http.route(["/my/orders/<int:order_id>/add_option/<int:option_id>"], type='http', auth="public", website=True)
|
||||
@http.route(["/my/orders/<int:order_id>/add_option/<int:option_id>"], type='json', auth="public", website=True)
|
||||
def add(self, order_id, option_id, access_token=None, **post):
|
||||
try:
|
||||
order_sudo = self._document_check_access('sale.order', order_id, access_token=access_token)
|
||||
@@ -76,5 +83,9 @@ class CustomerPortal(CustomerPortal):
|
||||
return request.redirect(order_sudo.get_portal_url())
|
||||
|
||||
option_sudo.add_option_to_order()
|
||||
|
||||
return request.redirect(option_sudo.order_id.get_portal_url(anchor='details'))
|
||||
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
|
||||
|
||||
@@ -4,56 +4,88 @@ odoo.define('sale_management.sale_management', function (require) {
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({
|
||||
selector: '.o_portal_sale_sidebar a.js_update_line_json',
|
||||
selector: '.o_portal_sale_sidebar',
|
||||
events: {
|
||||
'click': '_onClick',
|
||||
'click a.js_update_line_json': '_onClick',
|
||||
'click a.js_add_optional_products': '_onClickOptionalProduct',
|
||||
'change .js_quantity': '_onChangeQuantity'
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
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);
|
||||
});
|
||||
});
|
||||
},
|
||||
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 {Int} quantity, the new quantity of the line
|
||||
* If not present it will increment/decrement the existing quantity
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onChangeQuantity: function (quantity) {
|
||||
var href = this.$el.attr("href");
|
||||
var orderID = href.match(/my\/orders\/([0-9]+)/);
|
||||
var lineID = href.match(/update_line\/([0-9]+)/);
|
||||
var params = {
|
||||
'line_id': parseInt(lineID[1]),
|
||||
'remove': this.$el.is('[href*="remove"]'),
|
||||
'unlink': this.$el.is('[href*="unlink"]'),
|
||||
'input_quantity': quantity >= 0 ? quantity : false,
|
||||
};
|
||||
var token = href.match(/token=([\w\d-]*)/)[1];
|
||||
if (token) {
|
||||
params['access_token'] = token;
|
||||
}
|
||||
_onChangeQuantity(ev) {
|
||||
ev.preventDefault();
|
||||
let self = this,
|
||||
$target = $(ev.currentTarget),
|
||||
quantity = parseInt($target.val());
|
||||
|
||||
orderID = parseInt(orderID[1]);
|
||||
this._callUpdateLineRoute(orderID, params).then(this._updateOrderValues.bind(this));
|
||||
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: function (ev) {
|
||||
_onClick(ev) {
|
||||
ev.preventDefault();
|
||||
return this._onChangeQuantity();
|
||||
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
|
||||
@@ -64,43 +96,48 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({
|
||||
* @param {Object} params
|
||||
* @return {Deferred}
|
||||
*/
|
||||
_callUpdateLineRoute: function (order_id, params) {
|
||||
var url = "/my/orders/" + order_id + "/update_line_dict";
|
||||
_callUpdateLineRoute(order_id, params) {
|
||||
return this._rpc({
|
||||
route: url,
|
||||
route: "/my/orders/" + order_id + "/update_line_dict",
|
||||
params: params,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Processes data from the server to update the orderline UI
|
||||
*
|
||||
* @private
|
||||
* @param {Element} $orderLine: orderline element to update
|
||||
* @param {Object} data: contains order and line updated values
|
||||
*/
|
||||
_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);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 orderAmountUndiscounted = data.order_amount_undiscounted;
|
||||
var orderTotalsTable = $(data.order_totals_table);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
@@ -112,8 +149,8 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({
|
||||
if (orderAmountUndiscounted !== undefined) {
|
||||
this.elems.$orderAmountUndiscounted.text(orderAmountUndiscounted);
|
||||
}
|
||||
if (orderTotalsTable) {
|
||||
this.elems.$orderTotalsTable.find('table').replaceWith(orderTotalsTable);
|
||||
if ($orderTotalsTable.length) {
|
||||
this.elems.$orderTotalsTable.find('table').replaceWith($orderTotalsTable);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -124,18 +161,10 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({
|
||||
* @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"]').find('span, b');
|
||||
var $orderAmountTotal = $('[data-id="total_amount"]').find('span, b');
|
||||
var $orderAmountUndiscounted = $('[data-id="amount_undiscounted"]').find('span, b');
|
||||
_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');
|
||||
|
||||
if (!$orderAmountUntaxed.length) {
|
||||
$orderAmountUntaxed = $orderAmountTotal.eq(1);
|
||||
@@ -143,9 +172,6 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({
|
||||
}
|
||||
|
||||
return {
|
||||
$lineQuantity: this.$el.closest('.input-group').find('.js_quantity'),
|
||||
$linePriceSubTotal: $linePriceSubTotal,
|
||||
$linePriceTotal: $linePriceTotal,
|
||||
$orderAmountUntaxed: $orderAmountUntaxed,
|
||||
$orderAmountTotal: $orderAmountTotal,
|
||||
$orderTotalsTable: $('#total'),
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<xpath expr="//section[@id='details']//t[@t-if='not line.display_type']" position="inside">
|
||||
<td class="text-center" t-if="display_remove">
|
||||
<a t-att-href="sale_order.get_portal_url(suffix='/update_line/%s' % line.id, query_string='&unlink=True')" class="mb8 js_update_line_json d-print-none" t-if="sale_order.state in ('draft', 'sent') and line.sale_order_option_ids" aria-label="Remove" title="Remove">
|
||||
<a t-att-data-line-id="line.id" t-att-data-unlink="True" href="#" class="mb8 js_update_line_json d-print-none" t-if="sale_order.state in ('draft', 'sent') and line.sale_order_option_ids" aria-label="Remove" title="Remove">
|
||||
<span class="fa fa-trash-o"></span>
|
||||
</a>
|
||||
</td>
|
||||
@@ -65,8 +65,8 @@
|
||||
</strong>
|
||||
</td>
|
||||
<td class="text-center" t-if="sale_order.state in ['draft', 'sent'] and report_type == 'html'">
|
||||
<a t-att-href="sale_order.get_portal_url(suffix='/add_option/%s' % option.id)" class="mb8 d-print-none" aria-label="Add to cart" title="Add to cart">
|
||||
<span class="fa fa-lg fa-shopping-cart"/>
|
||||
<a t-att-data-option-id="option.id" href="#" class="mb8 js_add_optional_products d-print-none" aria-label="Add to cart" title="Add to cart">
|
||||
<span class="fa fa-shopping-cart"/>
|
||||
</a>
|
||||
</td>
|
||||
</t>
|
||||
@@ -83,16 +83,16 @@
|
||||
|
||||
<div class="input-group-prepend d-print-none">
|
||||
<span class="input-group-text">
|
||||
<a t-att-href="sale_order.get_portal_url(suffix='/update_line/%s' % line.id, query_string='&remove=True')" class="js_update_line_json" aria-label="Remove one" title="Remove one">
|
||||
<a t-att-data-line-id="line.id" t-att-data-remove="True" href="#" class="js_update_line_json" aria-label="Remove one" title="Remove one">
|
||||
<span class="fa fa-minus"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<!-- TODO add uom in this case too -->
|
||||
<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-line-id="line.id" t-att-value="line.product_uom_qty"/>
|
||||
<div class="input-group-append d-print-none">
|
||||
<span class="input-group-text">
|
||||
<a t-att-href="sale_order.get_portal_url(suffix='/update_line/%s' % line.id)" class="js_update_line_json" aria-label="Add one" title="Add one">
|
||||
<a t-att-data-line-id="line.id" href="#" class="js_update_line_json" aria-label="Add one" title="Add one">
|
||||
<span class="fa fa-plus"/>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user