diff --git a/addons/website_sale/controllers/__init__.py b/addons/website_sale/controllers/__init__.py index 562b16212ea..c61d1136c3f 100644 --- a/addons/website_sale/controllers/__init__.py +++ b/addons/website_sale/controllers/__init__.py @@ -2,6 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import backend -from . import delivery_main +from . import delivery from . import main from . import variant diff --git a/addons/website_sale/controllers/delivery_main.py b/addons/website_sale/controllers/delivery.py similarity index 75% rename from addons/website_sale/controllers/delivery_main.py rename to addons/website_sale/controllers/delivery.py index 8e022188d39..f3f76d3ff9a 100644 --- a/addons/website_sale/controllers/delivery_main.py +++ b/addons/website_sale/controllers/delivery.py @@ -12,22 +12,6 @@ from odoo.exceptions import UserError class WebsiteSaleDelivery(WebsiteSale): _express_checkout_shipping_route = '/shop/express/shipping_address_change' - @http.route() - def shop_payment(self, **post): - order = request.website.sale_get_order() - carrier_id = post.get('carrier_id') - keep_carrier = post.get('keep_carrier', False) - if keep_carrier: - keep_carrier = bool(int(keep_carrier)) - if carrier_id: - carrier_id = int(carrier_id) - if order: - order._check_carrier_quotation(force_carrier_id=carrier_id, keep_carrier=keep_carrier) - if carrier_id: - return request.redirect("/shop/payment") - - return super(WebsiteSaleDelivery, self).shop_payment(**post) - @http.route(['/shop/update_carrier'], type='json', auth='public', methods=['POST'], website=True) def update_eshop_carrier(self, **post): order = request.website.sale_get_order() @@ -62,18 +46,6 @@ class WebsiteSaleDelivery(WebsiteSale): res['error_message'] = rate['error_message'] return res - @http.route() - def cart(self, **post): - order = request.website.sale_get_order() - if order and order.carrier_id: - # Express checkout is based on the amout of the sale order. If there is already a - # delivery line, Express Checkout form will display and compute the price of the - # delivery two times (One already computed in the total amount of the SO and one added - # in the form while selecting the delivery carrier) - order._remove_delivery_line() - - return super().cart(**post) - @http.route( _express_checkout_shipping_route, type='json', auth='public', methods=['POST'], website=True, sitemap=False @@ -103,9 +75,7 @@ class WebsiteSaleDelivery(WebsiteSale): # Pricelist are recomputed every time the partner is changed. We don't want to recompute # the price with another pricelist at this state since the customer has already accepted # the amount and validated the payment. - order_sudo.env.remove_to_compute( - order_sudo._fields['pricelist_id'], order_sudo - ) + order_sudo.env.remove_to_compute(order_sudo._fields['pricelist_id'], order_sudo) elif order_sudo.partner_shipping_id.name.endswith(order_sudo.name): self._create_or_edit_partner( partial_shipping_address, @@ -123,15 +93,12 @@ class WebsiteSaleDelivery(WebsiteSale): child_partner_id = self._find_child_partner( order_sudo.partner_id.commercial_partner_id.id, partial_shipping_address ) - if child_partner_id: - order_sudo.partner_shipping_id = child_partner_id - else: - order_sudo.partner_shipping_id = self._create_or_edit_partner( + order_sudo.partner_shipping_id = child_partner_id or self._create_or_edit_partner( partial_shipping_address, type='delivery', parent_id=order_sudo.partner_id.id, name=_('Anonymous express checkout partner for order %s', order_sudo.name), - ) + ) # Returns the list of develivery carrier available for the sale order. return sorted([{ @@ -220,46 +187,6 @@ class WebsiteSaleDelivery(WebsiteSale): rate['price'] = taxes['total_included'] return rate - def order_lines_2_google_api(self, order_lines): - """ Transforms a list of order lines into a dict for google analytics """ - order_lines_not_delivery = order_lines.filtered(lambda line: not line.is_delivery) - return super(WebsiteSaleDelivery, self).order_lines_2_google_api(order_lines_not_delivery) - - def order_2_return_dict(self, order): - """ Returns the tracking_cart dict of the order for Google analytics """ - ret = super(WebsiteSaleDelivery, self).order_2_return_dict(order) - delivery_line = order.order_line.filtered('is_delivery') - if delivery_line: - ret['shipping'] = delivery_line.price_unit - return ret - - def _get_express_shop_payment_values(self, order, **kwargs): - values = super(WebsiteSaleDelivery, self)._get_express_shop_payment_values(order, **kwargs) - values['shipping_info_required'] = not order.only_services - values['shipping_address_update_route'] = self._express_checkout_shipping_route - return values - - def _get_shop_payment_values(self, order, **kwargs): - values = super(WebsiteSaleDelivery, self)._get_shop_payment_values(order, **kwargs) - has_storable_products = any(line.product_id.type in ['consu', 'product'] for line in order.order_line) - - if not order._get_delivery_methods() and has_storable_products: - values['errors'].append( - (_('Sorry, we are unable to ship your order'), - _('No shipping method is available for your current order and shipping address. ' - 'Please contact us for more information.'))) - - if has_storable_products: - if order.carrier_id and not order.delivery_rating_success: - order._remove_delivery_line() - - delivery_carriers = order._get_delivery_methods() - values['deliveries'] = delivery_carriers.sudo() - - values['delivery_has_storable'] = has_storable_products - values['delivery_action_id'] = request.env.ref('delivery.action_delivery_carrier_form').id - return values - def _update_website_sale_delivery_return(self, order, **post): Monetary = request.env['ir.qweb.field.monetary'] carrier_id = int(post['carrier_id']) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 7d52176400b..bce302b00cd 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -149,6 +149,7 @@ class Website(main.Website): class WebsiteSale(http.Controller): _express_checkout_route = '/shop/express_checkout' + _express_checkout_shipping_route = '/shop/express/shipping_address_change' WRITABLE_PARTNER_FIELDS = [ 'name', @@ -746,6 +747,12 @@ class WebsiteSale(http.Controller): revive: Revival method when abandoned cart. Can be 'merge' or 'squash' """ order = request.website.sale_get_order() + if order and order.carrier_id: + # Express checkout is based on the amout of the sale order. If there is already a + # delivery line, Express Checkout form will display and compute the price of the + # delivery two times (One already computed in the total amount of the SO and one added + # in the form while selecting the delivery carrier) + order._remove_delivery_line() if order and order.state != 'draft': request.session['sale_order_id'] = None order = request.website.sale_get_order() @@ -1225,9 +1232,7 @@ class WebsiteSale(http.Controller): order_sudo.partner_id.commercial_partner_id.id, billing_address ) order_sudo.partner_invoice_id = child_partner_id or self._create_or_edit_partner( - billing_address, - type='invoice', - parent_id=order_sudo.partner_id.id, + billing_address, type='invoice', parent_id=order_sudo.partner_id.id ) # In a non-express flow, `sale_last_order_id` would be added in the session before the @@ -1457,6 +1462,7 @@ class WebsiteSale(http.Controller): # ------------------------------------------------------ def _get_express_shop_payment_values(self, order, **kwargs): + request.session['sale_last_order_id'] = order.id payment_form_values = sale_portal.CustomerPortal._get_payment_values( self, order, website_id=request.website.id, is_express_checkout=True ) @@ -1472,6 +1478,11 @@ class WebsiteSale(http.Controller): }) if request.website.is_public_user(): payment_form_values['partner_id'] = -1 + if request.website.enabled_delivery: + payment_form_values.update({ + 'shipping_info_required': not order.only_services, + 'shipping_address_update_route': self._express_checkout_shipping_route, + }) return payment_form_values def _get_shop_payment_values(self, order, **kwargs): @@ -1489,7 +1500,29 @@ class WebsiteSale(http.Controller): 'transaction_route': f'/shop/payment/transaction/{order.id}', 'landing_route': '/shop/payment/validate', } - return {**portal_page_values, **payment_form_values} + values = {**portal_page_values, **payment_form_values} + if request.website.enabled_delivery: + has_storable_products = any( + line.product_id.type in ['consu', 'product'] for line in order.order_line + ) + if not order._get_delivery_methods() and has_storable_products: + values['errors'].append(( + _('Sorry, we are unable to ship your order'), + _('No shipping method is available for your current order and shipping address.' + ' Please contact us for more information.') + )) + + if has_storable_products: + if order.carrier_id and not order.delivery_rating_success: + order._remove_delivery_line() + values['deliveries'] = order._get_delivery_methods().sudo() + + values['delivery_has_storable'] = has_storable_products + values['delivery_action_id'] = request.env.ref( + 'delivery.action_delivery_carrier_form' + ).id + + return values @http.route('/shop/payment', type='http', auth='public', website=True, sitemap=False) def shop_payment(self, **post): @@ -1502,7 +1535,18 @@ class WebsiteSale(http.Controller): did go to a payment.provider website but closed the tab without paying / canceling """ + carrier_id = post.get('carrier_id') + keep_carrier = post.get('keep_carrier', False) + if keep_carrier: + keep_carrier = bool(int(keep_carrier)) + if carrier_id: + carrier_id = int(carrier_id) order = request.website.sale_get_order() + if order: + order._check_carrier_quotation(force_carrier_id=carrier_id, keep_carrier=keep_carrier) + if carrier_id: + return request.redirect("/shop/payment") + redirection = self.checkout_redirection(order) or self.checkout_check_address(order) if redirection: return redirection @@ -1661,7 +1705,7 @@ class WebsiteSale(http.Controller): def order_lines_2_google_api(self, order_lines): """ Transforms a list of order lines into a dict for google analytics """ ret = [] - for line in order_lines: + for line in order_lines.filtered(lambda line: not line.is_delivery): product = line.product_id ret.append({ 'item_id': product.barcode or product.id, @@ -1674,7 +1718,7 @@ class WebsiteSale(http.Controller): def order_2_return_dict(self, order): """ Returns the tracking_cart dict of the order for Google analytics basically defined to be inherited """ - return { + tracking_cart_dict = { 'transaction_id': order.id, 'affiliation': order.company_id.name, 'value': order.amount_total, @@ -1682,6 +1726,10 @@ class WebsiteSale(http.Controller): 'currency': order.currency_id.name, 'items': self.order_lines_2_google_api(order.order_line), } + delivery_line = order.order_line.filtered('is_delivery') + if delivery_line: + tracking_cart_dict['shipping'] = delivery_line.price_unit + return tracking_cart_dict @http.route(['/shop/country_infos/'], type='json', auth="public", methods=['POST'], website=True) def country_infos(self, country, mode, **kw): diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index adb92320d5f..8b4096a5b7d 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -104,6 +104,7 @@ class Website(models.Model): default="Not Available For Sale") contact_us_button_url = fields.Char(string="Contact Us Button URL", translate=True, default="/contactus") enabled_portal_reorder_button = fields.Boolean(string="Re-order From Portal") + enabled_delivery = fields.Boolean(string="Enable Shipping", compute='_compute_enabled_delivery') @api.depends('all_pricelist_ids') def _compute_pricelist_ids(self): @@ -127,6 +128,12 @@ class Website(models.Model): for website in self: website.currency_id = website.pricelist_id.currency_id or website.company_id.currency_id + def _compute_enabled_delivery(self): + for website in self: + website.enabled_delivery = bool(website.env['delivery.carrier'].sudo().search_count( + [('website_id', 'in', (False, website.id)), ('is_published', '=', True)], limit=1 + )) + # This method is cached, must not return records! See also #8795 @tools.ormcache( 'country_code', 'show_visible', diff --git a/addons/website_sale/security/ir.model.access.csv b/addons/website_sale/security/ir.model.access.csv index ba0931092d0..93e46bedc78 100644 --- a/addons/website_sale/security/ir.model.access.csv +++ b/addons/website_sale/security/ir.model.access.csv @@ -25,4 +25,3 @@ access_ecom_extra_fields_public,access_ecom_extra_field public,model_website_sal access_ecom_extra_fields_restricted_editor,access_ecom_extra_field restricted_editor,model_website_sale_extra_field,website.group_website_restricted_editor,1,1,1,1 access_website_base_unit_public,website.base.unit public,model_website_base_unit,,1,0,0,0 access_website_base_unit_sale_manager,website.base.unit sale manager,model_website_base_unit,sales_team.group_sale_manager,1,1,1,1 -access_delivery_carrier,delivery.carrier,delivery.model_delivery_carrier,sales_team.group_sale_salesman,1,0,0,0 diff --git a/addons/website_sale/tests/test_delivery_controller.py b/addons/website_sale/tests/test_delivery_controller.py index 60185ef680a..a346647af11 100644 --- a/addons/website_sale/tests/test_delivery_controller.py +++ b/addons/website_sale/tests/test_delivery_controller.py @@ -5,7 +5,7 @@ from unittest.mock import patch from odoo.exceptions import UserError from odoo.addons.payment.tests.common import PaymentCommon -from odoo.addons.website_sale.controllers.delivery_main import WebsiteSaleDelivery +from odoo.addons.website_sale.controllers.delivery import WebsiteSaleDelivery from odoo.addons.website.tools import MockRequest from odoo.tests import tagged diff --git a/addons/website_sale/tests/test_delivery_express_checkout_flows.py b/addons/website_sale/tests/test_delivery_express_checkout_flows.py index 492d2990f7d..890b581601f 100644 --- a/addons/website_sale/tests/test_delivery_express_checkout_flows.py +++ b/addons/website_sale/tests/test_delivery_express_checkout_flows.py @@ -10,7 +10,7 @@ from odoo.http import root from odoo.tests import tagged from odoo.addons.base.tests.common import HttpCaseWithUserDemo -from odoo.addons.website_sale.controllers.delivery_main import WebsiteSaleDelivery as WebsiteSaleDeliveryController +from odoo.addons.website_sale.controllers.delivery import WebsiteSaleDelivery as WebsiteSaleDeliveryController @tagged('post_install', '-at_install') diff --git a/addons/website_sale_loyalty/controllers/__init__.py b/addons/website_sale_loyalty/controllers/__init__.py index f7ed5e10f6d..0a19c28160b 100644 --- a/addons/website_sale_loyalty/controllers/__init__.py +++ b/addons/website_sale_loyalty/controllers/__init__.py @@ -1,4 +1,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import delivery_main +from . import delivery from . import main diff --git a/addons/website_sale_loyalty/controllers/delivery_main.py b/addons/website_sale_loyalty/controllers/delivery.py similarity index 96% rename from addons/website_sale_loyalty/controllers/delivery_main.py rename to addons/website_sale_loyalty/controllers/delivery.py index afbf6c26091..7f3ad2344fd 100644 --- a/addons/website_sale_loyalty/controllers/delivery_main.py +++ b/addons/website_sale_loyalty/controllers/delivery.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from odoo import http -from odoo.addons.website_sale.controllers.delivery_main import WebsiteSaleDelivery +from odoo.addons.website_sale.controllers.delivery import WebsiteSaleDelivery from odoo.http import request diff --git a/addons/website_sale_mondialrelay/controllers/controllers.py b/addons/website_sale_mondialrelay/controllers/controllers.py index bce15b98689..457f1a99a54 100644 --- a/addons/website_sale_mondialrelay/controllers/controllers.py +++ b/addons/website_sale_mondialrelay/controllers/controllers.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from odoo import http, _ from odoo.addons.website_sale.controllers.main import WebsiteSale, PaymentPortal -from odoo.addons.website_sale.controllers.delivery_main import WebsiteSaleDelivery +from odoo.addons.website_sale.controllers.delivery import WebsiteSaleDelivery from odoo.exceptions import AccessDenied, ValidationError, UserError from odoo.http import request