diff --git a/addons/website_sale_delivery/__manifest__.py b/addons/website_sale_delivery/__manifest__.py
index 13c8309b6d4..3be003a3954 100644
--- a/addons/website_sale_delivery/__manifest__.py
+++ b/addons/website_sale_delivery/__manifest__.py
@@ -12,6 +12,7 @@ Configure your own methods with a pricing grid or integrate with carriers for a
'views/website_sale_delivery_templates.xml',
'views/website_sale_delivery_views.xml',
'views/res_config_settings_views.xml',
+ 'views/locations_carrier_templates.xml',
'data/website_sale_delivery_data.xml'
],
'demo': [
diff --git a/addons/website_sale_delivery/controllers/main.py b/addons/website_sale_delivery/controllers/main.py
index 6d41e7537d2..91d841a488a 100644
--- a/addons/website_sale_delivery/controllers/main.py
+++ b/addons/website_sale_delivery/controllers/main.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import json
from odoo import http, _
from odoo.http import request
from odoo.addons.payment import utils as payment_utils
@@ -30,6 +31,7 @@ class WebsiteSaleDelivery(WebsiteSale):
@http.route(['/shop/update_carrier'], type='json', auth='public', methods=['POST'], website=True, csrf=False)
def update_eshop_carrier(self, **post):
order = request.website.sale_get_order()
+ order.access_point_address = {}
carrier_id = int(post['carrier_id'])
if order and carrier_id != order.carrier_id.id:
if any(tx.state not in ("canceled", "error", "draft") for tx in order.transaction_ids):
@@ -201,7 +203,44 @@ class WebsiteSaleDelivery(WebsiteSale):
order_sudo.currency_id,
),
} for carrier in order_sudo._get_delivery_methods()],
- key=lambda carrier: carrier['minorAmount'])
+ key=lambda carrier: carrier['minorAmount'])
+
+ @http.route('/shop/access_point/set', type='json', auth='public', methods=['POST'], csrf=False, website=True, sitemap=False)
+ def set_access_point(self, access_point_encoded):
+ order = request.website.sale_get_order()
+ if hasattr(order.carrier_id, order.carrier_id.delivery_type + '_use_locations'):
+ use_location = getattr(order.carrier_id, order.carrier_id.delivery_type + '_use_locations')
+ access_point = use_location and access_point_encoded or None
+ order.write({'access_point_address': access_point})
+
+ @http.route('/shop/access_point/get', type='json', auth='public', csrf=False, website=True, sitemap=False)
+ def get_access_point(self):
+ order = request.website.sale_get_order()
+ if not order.carrier_id.delivery_type:
+ return {}
+ order_location = json.loads(order.access_point_address) if order.access_point_address else False
+ if not order_location:
+ return {}
+ address = order_location['address']
+ return {order.carrier_id.delivery_type + '_access_point': address}
+
+ @http.route('/shop/access_point/close_locations', type='json', auth='public', csrf=False, website=True, sitemap=False)
+ def get_close_locations(self):
+ order = request.website.sale_get_order()
+ try:
+ error = {'error': 'No pick-up point available for that shipping address'}
+ if not hasattr(order.carrier_id, '_' + order.carrier_id.delivery_type + '_get_close_locations'):
+ return error
+ close_locations = getattr(order.carrier_id, '_' + order.carrier_id.delivery_type + '_get_close_locations')(order.partner_shipping_id)
+ partner_address = order.partner_shipping_id
+ inline_partner_address = ' '.join((part or '') for part in [partner_address.street, partner_address.street2, partner_address.zip, partner_address.country_id.code])
+ if len(close_locations) < 0:
+ return {'error': 'No pick-up point available for that shipping address'}
+ for location in close_locations:
+ location['address_stringified'] = json.dumps(location)
+ return {'close_locations': close_locations, 'partner_address': inline_partner_address}
+ except UserError as e:
+ return {'error': str(e)}
@staticmethod
def _get_rate(carrier, order, is_express_checkout_flow=False):
diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py
index b6c10287f5b..9b0a3ff01b8 100644
--- a/addons/website_sale_delivery/models/sale_order.py
+++ b/addons/website_sale_delivery/models/sale_order.py
@@ -11,6 +11,7 @@ class SaleOrder(models.Model):
compute='_compute_amount_delivery',
string='Delivery Amount',
help="The amount without tax.", store=True, tracking=True)
+ access_point_address = fields.Text('Delivery Point Address')
@api.depends('order_line.price_unit', 'order_line.tax_id', 'order_line.discount', 'order_line.product_uom_qty')
def _compute_amount_delivery(self):
diff --git a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
index 61a4b9561c4..ba5c2fc2a91 100644
--- a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
+++ b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
@@ -5,6 +5,7 @@ var core = require('web.core');
var publicWidget = require('web.public.widget');
var _t = core._t;
+var qweb = core.qweb;
var concurrency = require('web.concurrency');
var dp = new concurrency.DropPrevious();
@@ -12,7 +13,10 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({
selector: '.oe_website_sale',
events: {
'change select[name="shipping_id"]': '_onSetAddress',
- 'click #delivery_carrier .o_delivery_carrier_select': '_onCarrierClick',
+ 'click .o_delivery_carrier_select': '_onCarrierClick',
+ "click .o_address_select": "_onClickLocation",
+ "click .o_remove_order_location": "_onClickRemoveLocation",
+ "click .o_show_pickup_locations": "_onClickShowLocations",
},
/**
@@ -35,24 +39,196 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({
$carriers.filter(':checked').click();
}
- // Asynchronously retrieve every carrier price
- _.each($carriers, function (carrierInput, k) {
+ self._getCurrentLocation();
+
+ _.each($carriers, async function (carrierInput, k) {
self._showLoading($(carrierInput));
- self._rpc({
+ const result = await self._rpc({
route: '/shop/carrier_rate_shipment',
params: {
'carrier_id': carrierInput.value,
},
- }).then(self._handleCarrierUpdateResultBadge.bind(self));
+ })
+ self._handleCarrierUpdateResult(result)
});
-
+
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
+ /**
+ * @private
+ */
+ _getCurrentLocation: async function () {
+ const self=this;
+ const data = await self._rpc({
+ route: "/shop/access_point/get",
+ })
+ const order_locations = document.getElementsByClassName("o_order_location");
+ for (const order_loc of order_locations) {
+ const show_loc = order_loc.parentElement.nextElementSibling;
+ // We could end not having the property
+ let delivery_type=""
+ if(order_loc.parentElement.parentElement.parentElement.children[0].attributes[8]){
+ delivery_type = order_loc.parentElement.parentElement.parentElement.children[0].attributes[8].nodeValue;
+ }
+ if (!show_loc)
+ continue;
+ if (data[delivery_type+'_access_point']) {
+ order_loc.innerText = data[delivery_type+'_access_point']
+ order_loc.parentElement.classList.remove("d-none");
+ show_loc.classList.add("d-none");
+ break;
+ } else {
+ order_loc.parentElement.classList.add("d-none");
+ show_loc.classList.remove("d-none");
+ }
+ }
+ },
+ /**
+ * @private
+ */
+ _onClickRemoveLocation: async function (ev) {
+ ev.stopPropagation();
+ const self = this;
+ await this._rpc({
+ route: "/shop/access_point/set",
+ params: {
+ access_point_encoded: null,
+ },
+ })
+ self._getCurrentLocation();
+ },
+
+ /**
+ * @private
+ * @param {Event} ev
+ */
+ _onClickShowLocations: async function (ev) {
+ const self = this;
+ const show_pickup_locations = ev.currentTarget;
+ let modal = show_pickup_locations.nextElementSibling;
+ if (!show_pickup_locations.getElementsByClassName("link-primary").length)
+ return;
+ const should_load_content = modal.firstChild ? false : true;
+ while (modal.firstChild) {
+ modal.lastChild.remove();
+ }
+ if (!should_load_content)
+ return;
+
+ var delivery_type = ev.currentTarget.closest(".o_delivery_carrier_select").childNodes[1].attributes[8].nodeValue;
+
+ const delivery_type_id = ev.currentTarget.closest(".o_delivery_carrier_select").childNodes[1].value;
+ await this._checkCarrier(ev,delivery_type_id)
+
+ $(qweb.render(delivery_type + "_pickup_location_loading")).appendTo($(modal));
+ const data = await self._rpc({
+ route: "/shop/access_point/close_locations",
+ })
+ if (modal.firstChild)
+ modal.firstChild.remove();
+ if(!data || data && data.error || (data && data.close_locations && data.close_locations.length==0)){
+ const error_message = document.createElement("em");
+ error_message.classList.add("text-error");
+ error_message.innerText = data.error ? data.error : "No available Pick-Up Locations";
+ modal.appendChild(error_message);
+ return;
+ }
+
+ var list_to_render = delivery_type + "_pickup_location_list";
+ var data_to_render = {partner_address: data.partner_address};
+ data_to_render[delivery_type+"_pickup_locations"] = data.close_locations;
+ $(qweb.render(list_to_render, data_to_render)).appendTo($(modal));
+ this._displayCarrierDropper(ev);
+ },
+ /**
+ * @private
+ * @param {Object} obj // can be either an event or an objet returned by the controller
+ */
+ _displayCarrierDropper: async function (obj) {
+ let current_id = obj.carrier_id
+ const show_locations = document
+ .getElementById("delivery_carrier")
+ .getElementsByClassName("o_show_pickup_locations");
+
+ // We check if it's an event
+ if('currentTarget' in obj){
+ const is_full_page_event = !obj.currentTarget.closest(".o_delivery_carrier_select")
+ if(is_full_page_event)return;
+ for (const show_loc of show_locations){
+ this._specificDropperDisplay(show_loc);
+ }
+ }
+ else{
+ for (const show_loc of show_locations){
+ const current_carrier_id = show_loc.closest("li").getElementsByTagName("input")[0].value;
+ if (current_carrier_id == current_id) {
+ this._specificDropperDisplay(show_loc);
+ break;
+ }
+ }
+ }
+ },
+ /**
+ * @private
+ * @param {Object} doc_carrier //carrier element from document
+ */
+ _specificDropperDisplay: function (doc_carrier) {
+ if(!doc_carrier)
+ return;
+ if(!doc_carrier.closest("li").getElementsByTagName("input")[0].attributes[8])
+ return;
+ const current_carrier_type = doc_carrier.closest("li").getElementsByTagName("input")[0].attributes[8].nodeValue;
+ const location_still_loading = document.getElementById("#"+current_carrier_type+"_addresses")
+ if(location_still_loading)return
+ while (doc_carrier.firstChild) {
+ doc_carrier.lastChild.remove();
+ }
+ const current_carrier_checked = doc_carrier.closest("li").getElementsByTagName("input")[0].checked;
+ const span = document.createElement("em");
+ const is_pick_up_location_list = doc_carrier.nextElementSibling.childNodes.length > 0
+ if(current_carrier_checked){
+ const chevron_down = document.createElement("i");
+ if(is_pick_up_location_list)
+ chevron_down.classList.add("fa", "fa-angle-up");
+ else
+ chevron_down.classList.add("fa", "fa-angle-down");
+ span.textContent = "Pick-Up Locations ";
+ span.classList.add("link-primary");
+ span.appendChild(chevron_down);
+ }
+ else {
+ span.textContent = "select to see available Pick-Up Locations";
+ span.classList.add("text-muted");
+ }
+ doc_carrier.appendChild(span);
+ },
+ /**
+ * @private
+ * @param {Event} ev
+ */
+ _onClickLocation: async function (ev) {
+ ev.preventDefault();
+ const carrierId = ev.currentTarget.closest(".o_delivery_carrier_select").childNodes[1].value;
+ await this._checkCarrier(ev,carrierId)
+ const self = this;
+ const modal = ev.target.closest(".o_list_pickup_locations");
+ const encoded_location = ev.target.previousElementSibling.innerText;
+ await self._rpc({
+ route: "/shop/access_point/set",
+ params: {
+ access_point_encoded: encoded_location,
+ },
+ })
+ while (modal.firstChild) {
+ modal.lastChild.remove();
+ }
+ self._getCurrentLocation();
+ },
/**
* @private
* @param {jQuery} $carrierInput
@@ -100,6 +276,13 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({
if (result.new_amount_total_raw !== undefined) {
this._updateShippingCost(result.new_amount_total_raw);
}
+
+ var $carriers_inputs = $('#delivery_carrier input[name="delivery_type"]');
+ var carriers = [];
+ _.each($carriers_inputs, function (carrierInput, k) {
+ carriers.push(carrierInput.dataset.deliveryType);
+ })
+ this._displayCarrierDropper(result);
},
/**
* @private
@@ -108,7 +291,7 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({
_handleCarrierUpdateResultBadge: function (result) {
var $carrierBadge = $('#delivery_carrier input[name="delivery_type"][value=' + result.carrier_id + '] ~ .o_wsale_delivery_badge_price');
- if (result.status === true) {
+ if (result.status === true) {
// if free delivery (`free_over` field), show 'Free', not '$0'
if (result.is_free_delivery) {
$carrierBadge.text(_t('Free'));
@@ -130,7 +313,7 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({
* @private
* @param {Event} ev
*/
- _onCarrierClick: function (ev) {
+ _onCarrierClick: async function (ev) {
var $radio = $(ev.currentTarget).find('input[type="radio"]');
this._showLoading($radio);
$radio.prop("checked", true);
@@ -139,12 +322,26 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({
var disabledReasons = $payButton.data('disabled_reasons') || {};
disabledReasons.carrier_selection = true;
$payButton.data('disabled_reasons', disabledReasons);
- dp.add(this._rpc({
+ const data = await dp.add(this._rpc({
route: '/shop/update_carrier',
params: {
carrier_id: $radio.val(),
},
- })).then(this._handleCarrierUpdateResult.bind(this));
+ }))
+ this._handleCarrierUpdateResult(data);
+ },
+
+ _checkCarrier: async function (ev, carrier_id) {
+ ev.stopPropagation();
+ await dp.add(this._rpc({
+ route: '/shop/update_carrier',
+ params: {
+ carrier_id: carrier_id,
+ },
+ }))
+ var closestDocElement = ev.currentTarget.closest('.o_delivery_carrier_select');
+ var $radio = $(closestDocElement).find('input[type="radio"]');
+ $radio.prop("checked", true);
},
/**
* @private
diff --git a/addons/website_sale_delivery/views/locations_carrier_templates.xml b/addons/website_sale_delivery/views/locations_carrier_templates.xml
new file mode 100644
index 00000000000..8b2d0991f9d
--- /dev/null
+++ b/addons/website_sale_delivery/views/locations_carrier_templates.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/addons/website_sale_delivery/views/website_sale_delivery_templates.xml b/addons/website_sale_delivery/views/website_sale_delivery_templates.xml
index 792bd3beda9..7031f67886c 100644
--- a/addons/website_sale_delivery/views/website_sale_delivery_templates.xml
+++ b/addons/website_sale_delivery/views/website_sale_delivery_templates.xml
@@ -16,10 +16,10 @@
-
+
-
- Select to compute delivery rate
+
+ Select to compute delivery rate
@@ -28,14 +28,6 @@
-
-
Shipping Method:
-
-
-
-
-
-
@@ -43,8 +35,8 @@
-
Choose a delivery method
-
+
Choose a delivery method
+