From e5a77f2cc1789697d187477105d9da4bf0136b5c Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Wed, 29 Sep 2021 09:03:44 +0000 Subject: [PATCH] [FIX] website_sale_stock: use correct qty to show warning/error Without this commit, if you had: - 3 Qty for product P in WH1 - 10 Qty for product P in WH2 - no warehouse set on website - set show stock and prevent sales if not enough stock enabled And then: - Add 3 P in your cart - In cart, add more P, it would let you do it, while it shouldn't You would then be stuck on payment step as it would tell you only 3 are available. This commit improves 6b05fb2b21a3 by ensuring the warehouse_id used is the correct one (the one that will be used on the SO (required field)). That commit was fixing a case that was not supposed to happen[1], it is that case that should have been fixed (which is fixed by previous commit of this PR) [1]: it would fix the 'issue' when you add a product which has no stock available for the warehouse that will be used when creating the SO, by letting you through the warning, but you would then be stuck on the payment step as the qty check would be correctly done. The fix should have been to display the correct qty and prevent the add to cart in that case. closes odoo/odoo#78045 X-original-commit: 5f74b84f67222147b2eb7caadb2d831a059aae4e Signed-off-by: Romain Derie (rde) Signed-off-by: Adrien Widart Co-authored-by: Romain Derie Co-authored-by: Adrien Widart --- .../website_sale_stock/models/sale_order.py | 8 +-- ...st_website_sale_stock_product_warehouse.py | 52 +++++++++++++------ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/addons/website_sale_stock/models/sale_order.py b/addons/website_sale_stock/models/sale_order.py index 0a8e85905ae..961f35ef0fc 100644 --- a/addons/website_sale_stock/models/sale_order.py +++ b/addons/website_sale_stock/models/sale_order.py @@ -17,12 +17,8 @@ class SaleOrder(models.Model): for line in self.order_line: if line.product_id.type == 'product' and not line.product_id.allow_out_of_stock_order: cart_qty = sum(self.order_line.filtered(lambda p: p.product_id.id == line.product_id.id).mapped('product_uom_qty')) - # The quantity should be computed based on the warehouse of the website, not the - # warehouse of the SO. - website = self.env['website'].get_current_website() - avl_qty = line.product_id.with_context(warehouse=website.warehouse_id.id).free_qty - if cart_qty > avl_qty and (line_id == line.id): - qty = avl_qty - cart_qty + if cart_qty > line.product_id.with_context(warehouse=self.warehouse_id.id).free_qty and (line_id == line.id): + qty = line.product_id.with_context(warehouse=self.warehouse_id.id).free_qty - cart_qty new_val = super(SaleOrder, self)._cart_update(line.product_id.id, line.id, qty, 0, **kwargs) values.update(new_val) diff --git a/addons/website_sale_stock/tests/test_website_sale_stock_product_warehouse.py b/addons/website_sale_stock/tests/test_website_sale_stock_product_warehouse.py index e7a2aca8bb3..cc792581911 100644 --- a/addons/website_sale_stock/tests/test_website_sale_stock_product_warehouse.py +++ b/addons/website_sale_stock/tests/test_website_sale_stock_product_warehouse.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo.addons.website.tools import MockRequest from odoo.addons.sale.tests.test_sale_product_attribute_value_config import TestSaleProductAttributeValueCommon from odoo.tests import tagged @@ -9,31 +10,31 @@ from odoo.tests import tagged class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon): @classmethod - def setUpClass(self): + def setUpClass(cls): super().setUpClass() # Run the tests in another company, so the tests do not rely on the # database state (eg the default company's warehouse) - self.company = self.env['res.company'].create({'name': 'Company C'}) - self.env.user.company_id = self.company - self.website = self.env['website'].create({'name': 'Website Company C'}) - self.website.company_id = self.company + cls.company = cls.env['res.company'].create({'name': 'Company C'}) + cls.env.user.company_id = cls.company + cls.website = cls.env['website'].create({'name': 'Website Company C'}) + cls.website.company_id = cls.company # Set two warehouses (one was created on company creation) - self.warehouse_1 = self.env['stock.warehouse'].search([('company_id', '=', self.company.id)]) - self.warehouse_2 = self.env['stock.warehouse'].create({ + cls.warehouse_1 = cls.env['stock.warehouse'].search([('company_id', '=', cls.company.id)]) + cls.warehouse_2 = cls.env['stock.warehouse'].create({ 'name': 'Warehouse 2', 'code': 'WH2' }) # Create two stockable products - self.product_A = self.env['product.product'].create({ + cls.product_A = cls.env['product.product'].create({ 'name': 'Product A', 'allow_out_of_stock_order': False, 'type': 'product', 'default_code': 'E-COM1', }) - self.product_B = self.env['product.product'].create({ + cls.product_B = cls.env['product.product'].create({ 'name': 'Product B', 'allow_out_of_stock_order': False, 'type': 'product', @@ -41,17 +42,17 @@ class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon): }) # Add 10 Product A in WH1 and 15 Product 1 in WH2 - quants = self.env['stock.quant'].with_context(inventory_mode=True).create([{ - 'product_id': self.product_A.id, + quants = cls.env['stock.quant'].with_context(inventory_mode=True).create([{ + 'product_id': cls.product_A.id, 'inventory_quantity': qty, 'location_id': wh.lot_stock_id.id, - } for wh, qty in [(self.warehouse_1, 10.0), (self.warehouse_2, 15.0)]]) + } for wh, qty in [(cls.warehouse_1, 10.0), (cls.warehouse_2, 15.0)]]) # Add 10 Product 2 in WH2 - quants |= self.env['stock.quant'].with_context(inventory_mode=True).create({ - 'product_id': self.product_B.id, + quants |= cls.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': cls.product_B.id, 'inventory_quantity': 10.0, - 'location_id': self.warehouse_2.lot_stock_id.id, + 'location_id': cls.warehouse_2.lot_stock_id.id, }) quants.action_apply_inventory() @@ -79,3 +80,24 @@ class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon): # Check available quantity of product is according to warehouse self.assertEqual(combination_info['free_qty'], qty_b, "%s units of Product B should be available in warehouse %s" % (qty_b, wh)) + + def test_02_update_cart_with_multi_warehouses(self): + """ When the user updates his cart and increases a product quantity, if + this quantity is not available in the SO's warehouse, a warning should + be returned and the quantity updated to its maximum. """ + + so = self.env['sale.order'].create({ + 'partner_id': self.env.user.partner_id.id, + 'order_line': [(0, 0, { + 'name': self.product_A.name, + 'product_id': self.product_A.id, + 'product_uom_qty': 5, + 'product_uom': self.product_A.uom_id.id, + 'price_unit': self.product_A.list_price, + })] + }) + + with MockRequest(self.env, website=self.website, sale_order_id=so.id): + values = so._cart_update(product_id=self.product_A.id, line_id=so.order_line.id, set_qty=20) + self.assertTrue(values.get('warning', False)) + self.assertEqual(values.get('quantity'), 10)