mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-28 07:52:04 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,8 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_website_sale_stock_abandoned_cart_email
|
||||
from . import test_website_sale_stock_configurators
|
||||
from . import test_website_sale_stock_multilang
|
||||
from . import test_website_sale_stock_product_combo
|
||||
from . import test_website_sale_stock_product_product
|
||||
from . import test_website_sale_stock_product_template
|
||||
from . import test_website_sale_stock_product_warehouse
|
||||
from . import test_website_sale_stock_delivery
|
||||
from . import test_website_sale_stock_gmc
|
||||
from . import test_website_sale_stock_stock_notification
|
||||
from . import test_website_sale_stock_reorder_from_portal
|
||||
from . import test_website_sale_stock_sale_order_line
|
||||
from . import test_website_sale_stock_stock_message
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.website_sale.tests.common import WebsiteSaleCommon
|
||||
|
||||
|
||||
class WebsiteSaleStockCommon(WebsiteSaleCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.warehouse = cls.env['stock.warehouse'].search([('company_id', '=', cls.company.id)])
|
||||
|
||||
@classmethod
|
||||
def _add_product_qty_to_wh(cls, product_id, qty, loc_id):
|
||||
cls.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': product_id,
|
||||
'inventory_quantity': qty,
|
||||
'location_id': loc_id,
|
||||
}).action_apply_inventory()
|
||||
|
||||
@classmethod
|
||||
def _create_product(cls, **create_values):
|
||||
""" Override of `website_sale` to create storable products by default and restrict them from
|
||||
selling when out of stock.
|
||||
"""
|
||||
if create_values.get('type', 'consu') == 'consu': # Only for goods.
|
||||
if 'is_storable' not in create_values:
|
||||
create_values['is_storable'] = True
|
||||
if 'allow_out_of_stock_order' not in create_values:
|
||||
create_values['allow_out_of_stock_order'] = False
|
||||
return super()._create_product(**create_values)
|
||||
|
||||
@classmethod
|
||||
def _create_warehouse(cls, **kwargs):
|
||||
return cls.env['stock.warehouse'].create({
|
||||
'name': 'Test Warehouse',
|
||||
'code': 'TWH',
|
||||
**kwargs,
|
||||
})
|
||||
|
|
@ -1,26 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.website_sale.tests.test_website_sale_cart_abandoned import TestWebsiteSaleCartAbandonedCommon
|
||||
from odoo.tests.common import tagged
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockAbandonedCartEmail(TestWebsiteSaleCartAbandonedCommon):
|
||||
class TestWebsiteSaleStockAbandonedCartEmail(
|
||||
TestWebsiteSaleCartAbandonedCommon, WebsiteSaleStockCommon
|
||||
):
|
||||
def test_website_sale_stock_abandoned_cart_email(self):
|
||||
"""Make sure the send_abandoned_cart_email method sends the correct emails."""
|
||||
|
||||
website = self.env['website'].get_current_website()
|
||||
website.send_abandoned_cart_email = True
|
||||
website.write(
|
||||
{
|
||||
"send_abandoned_cart_email_activation_time": (
|
||||
datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)
|
||||
)
|
||||
- relativedelta(minutes=10)
|
||||
}
|
||||
)
|
||||
|
||||
storable_product_template = self.env['product.template'].create({
|
||||
'name': 'storable_product_template',
|
||||
'type': 'product',
|
||||
'allow_out_of_stock_order': False
|
||||
})
|
||||
storable_product_product = storable_product_template.product_variant_id
|
||||
storable_product_product = self._create_product()
|
||||
order_line = [[0, 0, {
|
||||
'name': 'The Product',
|
||||
'product_id': storable_product_product.id,
|
||||
|
|
@ -44,45 +51,10 @@ class TestWebsiteSaleStockAbandonedCartEmail(TestWebsiteSaleCartAbandonedCommon)
|
|||
sale_order.cart_recovery_email_sent = False
|
||||
|
||||
# Replenish the stock of the product
|
||||
self.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': storable_product_product.id,
|
||||
'inventory_quantity': 10.0,
|
||||
'location_id': self.env.user._get_default_warehouse_id().lot_stock_id.id,
|
||||
}).action_apply_inventory()
|
||||
|
||||
self.assertTrue(self.send_mail_patched(sale_order.id))
|
||||
|
||||
company = self.env['res.company'].create({'name': 'Company C'})
|
||||
self.env.user.company_id = company
|
||||
website_1 = self.env['website'].create({
|
||||
'name': 'Website Company C',
|
||||
'company_id': company.id,
|
||||
'send_abandoned_cart_email': True,
|
||||
})
|
||||
warehouse_1 = self.env['stock.warehouse'].search([('company_id', '=', company.id)])
|
||||
product = self.env['product.product'].create({
|
||||
'name': 'Product',
|
||||
'allow_out_of_stock_order': False,
|
||||
'type': 'product',
|
||||
'default_code': 'E-COM1',
|
||||
})
|
||||
self.env['stock.quant'].with_context(inventory_mode=True).create([{
|
||||
'product_id': product.id,
|
||||
'inventory_quantity': 25.0,
|
||||
'location_id': warehouse_1.lot_stock_id.id,
|
||||
}]).action_apply_inventory()
|
||||
|
||||
sale_order = self.env['sale.order'].create({
|
||||
'partner_id': self.customer.id,
|
||||
'website_id': website_1.id,
|
||||
'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
|
||||
minutes=1),
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': 5,
|
||||
}),
|
||||
],
|
||||
})
|
||||
self._add_product_qty_to_wh(
|
||||
storable_product_product.id,
|
||||
10,
|
||||
self.env.user._get_default_warehouse_id().lot_stock_id.id,
|
||||
)
|
||||
|
||||
self.assertTrue(self.send_mail_patched(sale_order.id))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import HttpCase, tagged
|
||||
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockConfigurators(HttpCase, WebsiteSaleStockCommon):
|
||||
|
||||
def test_website_sale_stock_product_configurator(self):
|
||||
stock_attribute = self.env['product.attribute'].create({
|
||||
'name': "Stock",
|
||||
'value_ids': [
|
||||
Command.create({'name': "Out of stock"}),
|
||||
Command.create({'name': "In stock"}),
|
||||
]
|
||||
})
|
||||
optional_product = self.env['product.template'].create({
|
||||
'name': "Optional product",
|
||||
'website_published': True,
|
||||
'is_storable': True,
|
||||
'allow_out_of_stock_order': False,
|
||||
'attribute_line_ids': [
|
||||
Command.create({
|
||||
'attribute_id': stock_attribute.id,
|
||||
'value_ids': [Command.set(stock_attribute.value_ids.ids)],
|
||||
})
|
||||
],
|
||||
})
|
||||
main_product = self.env['product.product'].create({
|
||||
'name': "Main product",
|
||||
'website_published': True,
|
||||
'is_storable': True,
|
||||
'allow_out_of_stock_order': False,
|
||||
'optional_product_ids': [Command.set(optional_product.ids)],
|
||||
})
|
||||
self.env['stock.quant'].create([
|
||||
{
|
||||
'product_id': optional_product.product_variant_ids[1].id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 10,
|
||||
}, {
|
||||
'product_id': main_product.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 10,
|
||||
},
|
||||
])
|
||||
self.start_tour('/', 'website_sale_stock_product_configurator')
|
||||
|
||||
def test_website_sale_stock_combo_configurator(self):
|
||||
product = self._create_product(name="Test product")
|
||||
self.env['stock.quant'].create({
|
||||
'product_id': product.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 2,
|
||||
})
|
||||
combo = self.env['product.combo'].create({
|
||||
'name': "Test combo",
|
||||
'combo_item_ids': [
|
||||
Command.create({'product_id': product.id}),
|
||||
Command.create({'product_id': self._create_product(
|
||||
allow_out_of_stock_order=True, is_storable=False
|
||||
).id}),
|
||||
],
|
||||
})
|
||||
self._create_product(
|
||||
name="Combo product",
|
||||
type='combo',
|
||||
combo_ids=[Command.link(combo.id)]
|
||||
)
|
||||
self.start_tour('/', 'website_sale_stock_combo_configurator')
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests import tagged
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.payment.tests.common import PaymentCommon
|
||||
from odoo.addons.website_sale.controllers.cart import Cart
|
||||
from odoo.addons.website_sale.controllers.main import WebsiteSale
|
||||
from odoo.addons.website_sale.tests.common import MockRequest, WebsiteSaleCommon
|
||||
from odoo.addons.delivery.tests.common import DeliveryCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockDeliveryController(PaymentCommon, WebsiteSaleCommon, DeliveryCommon):
|
||||
|
||||
def test_validate_payment_with_no_available_delivery_method(self):
|
||||
"""
|
||||
An error should be raised if you try to validate an order with a storable
|
||||
product without any delivery method available
|
||||
"""
|
||||
storable_product = self.env['product.product'].create([{
|
||||
'name': 'Storable Product',
|
||||
'sale_ok': True,
|
||||
'is_storable': True,
|
||||
'website_published': True,
|
||||
}])
|
||||
carriers = self.env['delivery.carrier'].search([])
|
||||
carriers.write({'website_published': False})
|
||||
|
||||
WebsiteSaleCartController = Cart()
|
||||
WebsiteSaleController = WebsiteSale()
|
||||
with MockRequest(self.env, website=self.website):
|
||||
WebsiteSaleCartController.add_to_cart(
|
||||
product_template_id=storable_product.product_tmpl_id,
|
||||
product_id=storable_product.id,
|
||||
quantity=1,
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
WebsiteSaleController.shop_payment_validate()
|
||||
|
||||
def test_validate_order_out_of_stock_zero_price(self):
|
||||
"""
|
||||
An error should be raised if you try to validate an order for
|
||||
an out of stock product with 0 price
|
||||
"""
|
||||
WebsiteSaleController = WebsiteSale()
|
||||
storable_product = self.env['product.product'].create({
|
||||
'name': 'Storable Product',
|
||||
'sale_ok': True,
|
||||
'is_storable': True,
|
||||
'website_published': True,
|
||||
'allow_out_of_stock_order': False,
|
||||
'lst_price': 0,
|
||||
})
|
||||
sale_order = self.env['sale.order'].create({
|
||||
'partner_id': self.partner.id,
|
||||
'order_line': [Command.create({
|
||||
'product_id': storable_product.id,
|
||||
'product_uom_qty': 12.0,
|
||||
})],
|
||||
'carrier_id': self.free_delivery.id,
|
||||
})
|
||||
self.free_delivery.write({'website_published': True})
|
||||
self.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': storable_product.id,
|
||||
'inventory_quantity': 10.0,
|
||||
'location_id': self.env.user._get_default_warehouse_id().lot_stock_id.id,
|
||||
}).action_apply_inventory()
|
||||
|
||||
with MockRequest(self.env, website=self.website):
|
||||
request.cart = sale_order
|
||||
with self.assertRaises(ValidationError):
|
||||
WebsiteSaleController.shop_payment_validate()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.website_sale.tests.common_gmc import WebsiteSaleGMCCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockGMC(WebsiteSaleGMCCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.website.warehouse_id = cls.env.ref('stock.warehouse0')
|
||||
cls.stock_loc = cls.website.warehouse_id.lot_stock_id
|
||||
cls.supplier_loc = cls.env.ref('stock.stock_location_suppliers')
|
||||
(cls.blue_sofa + cls.red_sofa + cls.blanket).write({
|
||||
'is_storable': True,
|
||||
'allow_out_of_stock_order': False,
|
||||
})
|
||||
cls.red_sofa.allow_out_of_stock_order = True
|
||||
cls.env['stock.quant'].create({
|
||||
'product_id': cls.blue_sofa.id,
|
||||
'quantity': 10.0,
|
||||
'location_id': cls.stock_loc.id,
|
||||
})
|
||||
|
||||
def test_gmc_items_availability_check_stock(self):
|
||||
self.update_items()
|
||||
|
||||
self.assertEqual('in_stock', self.blue_sofa_item['availability'])
|
||||
self.assertEqual('out_of_stock', self.items[self.blanket]['availability'])
|
||||
self.assertEqual('in_stock', self.red_sofa_item['availability']) # allow_out_of_stock_order
|
||||
|
||||
def test_gmc_items_keep_website_stock_separate(self):
|
||||
self.blue_sofa.allow_out_of_stock_order = False
|
||||
# setup second website with seperate stock
|
||||
warehouse2 = self.env['stock.warehouse'].create({'name': 'Stock 2', 'code': 'WH2'})
|
||||
self.gmc_feed.website_id = self.env['website'].create({
|
||||
'name': 'Website Test 2',
|
||||
'domain': 'https://my-website.net',
|
||||
'warehouse_id': warehouse2.id,
|
||||
})
|
||||
|
||||
self.update_items()
|
||||
|
||||
self.assertEqual('out_of_stock', self.red_sofa_item['availability'])
|
||||
|
|
@ -17,7 +17,7 @@ class TestWebsiteSaleStockMultilang(HttpCase):
|
|||
# Configure product: out-of-stock message in EN and FR
|
||||
unavailable_product = self.env['product.product'].create({
|
||||
'name': 'unavailable_product',
|
||||
'type': 'product',
|
||||
'is_storable': True,
|
||||
'allow_out_of_stock_order': False,
|
||||
'sale_ok': True,
|
||||
'website_published': True,
|
||||
|
|
@ -27,5 +27,4 @@ class TestWebsiteSaleStockMultilang(HttpCase):
|
|||
unavailable_product.update_field_translations('out_of_stock_message', {
|
||||
'fr_FR': {'Out of stock': 'Hors-stock'},
|
||||
})
|
||||
|
||||
self.start_tour("/fr/shop?search=unavailable", 'website_sale_stock_multilang')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
from odoo.addons.website_sale.tests.common import MockRequest
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockProductCombo(HttpCase, WebsiteSaleStockCommon):
|
||||
|
||||
def test_get_max_quantity_with_max(self):
|
||||
product_a = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
product_b = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
self.env['stock.quant'].create([
|
||||
{
|
||||
'product_id': product_a.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 5,
|
||||
}, {
|
||||
'product_id': product_b.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 10,
|
||||
},
|
||||
])
|
||||
combo = self.env['product.combo'].create({
|
||||
'name': "Test combo",
|
||||
'combo_item_ids': [
|
||||
Command.create({'product_id': product_a.id}),
|
||||
Command.create({'product_id': product_b.id}),
|
||||
],
|
||||
})
|
||||
self.cart.order_line = [Command.create({'product_id': product_b.id, 'product_uom_qty': 3})]
|
||||
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
|
||||
self.assertEqual(combo._get_max_quantity(self.website, self.cart), 7)
|
||||
|
||||
def test_get_max_quantity_without_max(self):
|
||||
product_a = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
product_b = self._create_product(is_storable=True, allow_out_of_stock_order=True)
|
||||
self.env['stock.quant'].create({
|
||||
'product_id': product_a.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 5,
|
||||
})
|
||||
combo = self.env['product.combo'].create({
|
||||
'name': "Test combo",
|
||||
'combo_item_ids': [
|
||||
Command.create({'product_id': product_a.id}),
|
||||
Command.create({'product_id': product_b.id}),
|
||||
],
|
||||
})
|
||||
|
||||
self.assertIsNone(combo._get_max_quantity(self.website, self.cart))
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
from odoo.addons.website_sale.tests.common import MockRequest
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockProductProduct(HttpCase, WebsiteSaleStockCommon):
|
||||
|
||||
def test_get_max_quantity_with_max(self):
|
||||
product = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
self.env['stock.quant'].create({
|
||||
'product_id': product.id, 'location_id': self.warehouse.lot_stock_id.id, 'quantity': 5
|
||||
})
|
||||
self.cart.order_line = [Command.create({'product_id': product.id, 'product_uom_qty': 3})]
|
||||
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
|
||||
self.assertEqual(product._get_max_quantity(self.website, self.cart), 2)
|
||||
|
||||
def test_get_max_quantity_without_max(self):
|
||||
product = self._create_product(is_storable=True, allow_out_of_stock_order=True)
|
||||
|
||||
self.assertIsNone(product._get_max_quantity(self.website, self.cart))
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import HttpCase, tagged
|
||||
|
||||
from odoo.addons.website_sale.tests.common import MockRequest
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockProductTemplate(HttpCase, WebsiteSaleStockCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.product_oos_order_allowed = cls._create_product(
|
||||
is_storable=True,
|
||||
allow_out_of_stock_order=True,
|
||||
)
|
||||
cls.product_oos_order_not_allowed = cls._create_product(
|
||||
is_storable=True,
|
||||
allow_out_of_stock_order=False,
|
||||
)
|
||||
|
||||
def test_website_sale_stock_get_additional_configurator_data(self):
|
||||
product = self.product_oos_order_not_allowed
|
||||
self.env['stock.quant'].create({
|
||||
'product_id': product.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 10,
|
||||
})
|
||||
|
||||
env = self.env(user=self.public_user)
|
||||
with MockRequest(env, website=self.website.with_env(env)):
|
||||
configurator_data = self.env['product.template']._get_additional_configurator_data(
|
||||
product_or_template=product,
|
||||
date=datetime(2000, 1, 1),
|
||||
currency=self.currency,
|
||||
pricelist=self.pricelist,
|
||||
)
|
||||
|
||||
self.assertEqual(configurator_data['free_qty'], 10)
|
||||
|
||||
def test_get_additional_combination_info_max_combo_quantity_with_max(self):
|
||||
product_a = self.product_oos_order_not_allowed
|
||||
product_b = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
product_c = self.product_oos_order_allowed
|
||||
self.env['stock.quant'].create([
|
||||
{
|
||||
'product_id': product_a.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 5,
|
||||
}, {
|
||||
'product_id': product_b.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 10,
|
||||
},
|
||||
])
|
||||
combo_a, combo_b, combo_c = self.env['product.combo'].create([
|
||||
{'name': "Combo A", 'combo_item_ids': [Command.create({'product_id': product_a.id})]},
|
||||
{'name': "Combo B", 'combo_item_ids': [Command.create({'product_id': product_b.id})]},
|
||||
{'name': "Combo C", 'combo_item_ids': [Command.create({'product_id': product_c.id})]},
|
||||
])
|
||||
combo_product = self._create_product(
|
||||
type='combo',
|
||||
combo_ids=[
|
||||
Command.link(combo_a.id), Command.link(combo_b.id), Command.link(combo_c.id)
|
||||
],
|
||||
)
|
||||
self.cart.order_line = [Command.create({'product_id': product_a.id, 'product_uom_qty': 3})]
|
||||
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
|
||||
combination_info = self.env['product.template'].with_context(
|
||||
website_sale_stock_get_quantity=True
|
||||
)._get_additionnal_combination_info(
|
||||
combo_product,
|
||||
quantity=3,
|
||||
uom=combo_product.uom_id,
|
||||
date=datetime(2000, 1, 1),
|
||||
website=self.website
|
||||
)
|
||||
|
||||
self.assertEqual(combination_info['max_combo_quantity'], 2)
|
||||
|
||||
def test_get_additional_combination_info_max_combo_quantity_without_max(self):
|
||||
product = self.product_oos_order_allowed
|
||||
combo = self.env['product.combo'].create({
|
||||
'name': "Test combo", 'combo_item_ids': [Command.create({'product_id': product.id})]
|
||||
})
|
||||
combo_product = self._create_product(type='combo', combo_ids=[Command.link(combo.id)])
|
||||
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
|
||||
combination_info = self.env['product.template'].with_context(
|
||||
website_sale_stock_get_quantity=True
|
||||
)._get_additionnal_combination_info(
|
||||
combo_product,
|
||||
quantity=3,
|
||||
uom=combo_product.uom_id,
|
||||
date=datetime(2000, 1, 1),
|
||||
website=self.website
|
||||
)
|
||||
|
||||
self.assertNotIn('max_combo_quantity', combination_info)
|
||||
|
||||
def test_get_additional_combination_info_free_quantity_is_integer(self):
|
||||
self._add_product_qty_to_wh(
|
||||
self.product_oos_order_not_allowed.id,
|
||||
9,
|
||||
self.warehouse.lot_stock_id.id,
|
||||
)
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
|
||||
combination_info = self.env['product.template'].with_context(
|
||||
website_sale_stock_get_quantity=True,
|
||||
)._get_additionnal_combination_info(
|
||||
self.product_oos_order_not_allowed,
|
||||
quantity=9,
|
||||
uom=self.env.ref('uom.product_uom_pack_6'),
|
||||
date=datetime(2000, 1, 1),
|
||||
website=self.website,
|
||||
)
|
||||
self.assertEqual(combination_info['free_qty'], 1)
|
||||
|
|
@ -1,13 +1,19 @@
|
|||
# -*- 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
|
||||
|
||||
from odoo.addons.product.tests.test_product_attribute_value_config import (
|
||||
TestProductAttributeValueCommon,
|
||||
)
|
||||
from odoo.addons.website_sale.tests.common import MockRequest
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon):
|
||||
class TestWebsiteSaleStockProductWarehouse(
|
||||
TestProductAttributeValueCommon, WebsiteSaleStockCommon
|
||||
):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
|
@ -19,67 +25,44 @@ class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon):
|
|||
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)
|
||||
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
|
||||
cls.product_A = cls.env['product.product'].create({
|
||||
'name': 'Product A',
|
||||
'allow_out_of_stock_order': False,
|
||||
'type': 'product',
|
||||
'default_code': 'E-COM1',
|
||||
})
|
||||
|
||||
cls.product_B = cls.env['product.product'].create({
|
||||
'name': 'Product B',
|
||||
'allow_out_of_stock_order': False,
|
||||
'type': 'product',
|
||||
'default_code': 'E-COM2',
|
||||
})
|
||||
# Set two warehouses (one was created on company creation)
|
||||
cls.warehouse_1 = cls.env['stock.warehouse'].search([
|
||||
('company_id', '=', cls.company.id)
|
||||
])
|
||||
cls.warehouse_2 = cls._create_warehouse()
|
||||
cls.product_A = cls._create_product()
|
||||
cls.product_B = cls._create_product()
|
||||
cls.test_env = cls.env['base'].with_context(
|
||||
website_id=cls.website.id,
|
||||
website_sale_stock_get_quantity=True,
|
||||
).env
|
||||
|
||||
# Add 10 Product A in WH1 and 15 Product 1 in WH2
|
||||
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 [(cls.warehouse_1, 10.0), (cls.warehouse_2, 15.0)]])
|
||||
cls._add_product_qty_to_wh(cls.product_A.id, 10, cls.warehouse_1.lot_stock_id.id)
|
||||
cls._add_product_qty_to_wh(cls.product_A.id, 15, cls.warehouse_2.lot_stock_id.id)
|
||||
|
||||
# Add 10 Product 2 in WH2
|
||||
quants |= cls.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': cls.product_B.id,
|
||||
'inventory_quantity': 10.0,
|
||||
'location_id': cls.warehouse_2.lot_stock_id.id,
|
||||
})
|
||||
quants.action_apply_inventory()
|
||||
cls._add_product_qty_to_wh(cls.product_B.id, 10, cls.warehouse_2.lot_stock_id.id)
|
||||
|
||||
def test_01_get_combination_info(self):
|
||||
""" Checked that correct product quantity is shown in website according
|
||||
to the warehouse which is set in current website.
|
||||
- Set Warehouse 1, Warehouse 2 or none in website and:
|
||||
- Check available quantity of Product A and Product B in website
|
||||
When the user doesn't set any warehouse, the module should still select
|
||||
a default one.
|
||||
"""
|
||||
def test_get_combination_info_free_qty_when_warehouse_is_set(self):
|
||||
self.website.warehouse_id = self.warehouse_2
|
||||
test_env = self.test_env
|
||||
with MockRequest(test_env, website=self.website.with_env(test_env)):
|
||||
combination_info = self.product_A.with_env(test_env)._get_combination_info_variant()
|
||||
self.assertEqual(combination_info['free_qty'], 15)
|
||||
with MockRequest(test_env, website=self.website.with_env(test_env)):
|
||||
combination_info = self.product_B.with_env(test_env)._get_combination_info_variant()
|
||||
self.assertEqual(combination_info['free_qty'], 10)
|
||||
|
||||
for wh, qty_a, qty_b in [(self.warehouse_1, 10, 0), (self.warehouse_2, 15, 10), (False, 10, 0)]:
|
||||
# set warehouse_id
|
||||
self.website.warehouse_id = wh
|
||||
|
||||
product = self.product_A.with_context(website_id=self.website.id)
|
||||
combination_info = product.product_tmpl_id.with_context(website_sale_stock_get_quantity=True)._get_combination_info()
|
||||
|
||||
# Check available quantity of product is according to warehouse
|
||||
self.assertEqual(combination_info['free_qty'], qty_a, "%s units of Product A should be available in warehouse %s" % (qty_a, wh))
|
||||
|
||||
product = self.product_B.with_context(website_id=self.website.id)
|
||||
combination_info = product.product_tmpl_id.with_context(website_sale_stock_get_quantity=True)._get_combination_info()
|
||||
|
||||
# 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_get_combination_info_free_qty_when_no_warehouse_is_set(self):
|
||||
self.website.warehouse_id = False
|
||||
test_env = self.test_env
|
||||
with MockRequest(test_env, website=self.website.with_env(test_env)):
|
||||
combination_info = self.product_A.with_env(test_env)._get_combination_info_variant()
|
||||
self.assertEqual(combination_info['free_qty'], 25)
|
||||
with MockRequest(test_env, website=self.website.with_env(test_env)):
|
||||
combination_info = self.product_B.with_env(test_env)._get_combination_info_variant()
|
||||
self.assertEqual(combination_info['free_qty'], 10)
|
||||
|
||||
def test_02_update_cart_with_multi_warehouses(self):
|
||||
""" When the user updates his cart and increases a product quantity, if
|
||||
|
|
@ -87,20 +70,25 @@ class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon):
|
|||
be returned and the quantity updated to its maximum. """
|
||||
|
||||
so = self.env['sale.order'].create({
|
||||
'website_id': self.website.id,
|
||||
'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):
|
||||
website_so = self.website.sale_get_order()
|
||||
self.assertEqual(website_so.order_line.product_id.virtual_available, 10, "This quantity should be based on SO's warehouse")
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=so.id) as req:
|
||||
website_so = req.cart
|
||||
self.assertEqual(website_so, so)
|
||||
self.assertEqual(
|
||||
website_so.order_line.product_id.virtual_available,
|
||||
25,
|
||||
"This quantity should be based on all warehouses.",
|
||||
)
|
||||
|
||||
values = so._cart_update(product_id=self.product_A.id, line_id=so.order_line.id, set_qty=20)
|
||||
values = so._cart_update_line_quantity(line_id=so.order_line.id, quantity=30)
|
||||
self.assertTrue(values.get('warning', False))
|
||||
self.assertEqual(values.get('quantity'), 10)
|
||||
self.assertEqual(values.get('quantity'), 25)
|
||||
|
|
|
|||
|
|
@ -1,52 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockReorderFromPortal(HttpCase):
|
||||
class TestWebsiteSaleStockReorderFromPortal(HttpCase, WebsiteSaleStockCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env['website'].get_current_website().enabled_portal_reorder_button = True
|
||||
|
||||
cls.available_product = cls.env['product.product'].create({
|
||||
'name': 'available_product',
|
||||
'type': 'product',
|
||||
'allow_out_of_stock_order': False,
|
||||
'sale_ok': True,
|
||||
'website_published': True,
|
||||
})
|
||||
cls.unavailable_product = cls.env['product.product'].create({
|
||||
'name': 'unavailable_product',
|
||||
'type': 'product',
|
||||
'allow_out_of_stock_order': False,
|
||||
'sale_ok': True,
|
||||
'website_published': True,
|
||||
})
|
||||
cls.partially_available_product = cls.env['product.product'].create({
|
||||
'name': 'partially_available_product',
|
||||
'type': 'product',
|
||||
'allow_out_of_stock_order': False,
|
||||
'sale_ok': True,
|
||||
'website_published': True,
|
||||
})
|
||||
cls.available_product = cls._create_product(name='available_product')
|
||||
cls.unavailable_product = cls._create_product(name='unavailable_product')
|
||||
cls.partially_available_product = cls._create_product(
|
||||
name='partially_available_product'
|
||||
)
|
||||
user_admin = cls.env.ref('base.user_admin')
|
||||
order = cls.env['sale.order'].create({
|
||||
'partner_id': user_admin.partner_id.id,
|
||||
'state': 'sale',
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
Command.create({
|
||||
'product_id': cls.available_product.id,
|
||||
'product_uom_qty': 1,
|
||||
}),
|
||||
(0, 0, {
|
||||
Command.create({
|
||||
'product_id': cls.unavailable_product.id,
|
||||
'product_uom_qty': 1,
|
||||
}),
|
||||
(0, 0, {
|
||||
Command.create({
|
||||
'product_id': cls.partially_available_product.id,
|
||||
'product_uom_qty': 2,
|
||||
})
|
||||
|
|
@ -54,16 +40,9 @@ class TestWebsiteSaleStockReorderFromPortal(HttpCase):
|
|||
})
|
||||
order.message_subscribe(user_admin.partner_id.ids)
|
||||
|
||||
cls.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': cls.available_product.id,
|
||||
'inventory_quantity': 10.0,
|
||||
'location_id': 8,
|
||||
}).action_apply_inventory()
|
||||
cls.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': cls.partially_available_product.id,
|
||||
'inventory_quantity': 1.0,
|
||||
'location_id': 8,
|
||||
}).action_apply_inventory()
|
||||
stock_loc_id = cls.env.ref('stock.stock_location_stock').id
|
||||
cls._add_product_qty_to_wh(cls.available_product.id, 10, stock_loc_id)
|
||||
cls._add_product_qty_to_wh(cls.partially_available_product.id, 3, stock_loc_id)
|
||||
|
||||
def test_website_sale_stock_reorder_from_portal_stock(self):
|
||||
self.start_tour("/", 'website_sale_stock_reorder_from_portal', login='admin')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
from odoo.addons.website_sale.tests.common import MockRequest
|
||||
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockSaleOrderLine(HttpCase, WebsiteSaleStockCommon):
|
||||
|
||||
def test_get_max_line_qty_with_max(self):
|
||||
product_a = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
product_b = self._create_product(is_storable=True, allow_out_of_stock_order=False)
|
||||
self.env['stock.quant'].create([
|
||||
{
|
||||
'product_id': product_a.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 5,
|
||||
}, {
|
||||
'product_id': product_b.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'quantity': 10,
|
||||
},
|
||||
])
|
||||
combo_a, combo_b = self.env['product.combo'].create([
|
||||
{'name': "Combo A", 'combo_item_ids': [Command.create({'product_id': product_a.id})]},
|
||||
{'name': "Combo B", 'combo_item_ids': [Command.create({'product_id': product_b.id})]},
|
||||
])
|
||||
combo_product = self._create_product(
|
||||
type='combo', combo_ids=[Command.link(combo_a.id), Command.link(combo_b.id)]
|
||||
)
|
||||
combo_product_line = self.env['sale.order.line'].create({
|
||||
'order_id': self.cart.id, 'product_id': combo_product.id, 'product_uom_qty': 3
|
||||
})
|
||||
combo_item_line_a, _combo_item_line_b = self.env['sale.order.line'].create([
|
||||
{
|
||||
'order_id': self.cart.id,
|
||||
'product_id': product_a.id,
|
||||
'product_uom_qty': 3,
|
||||
'linked_line_id': combo_product_line.id,
|
||||
'combo_item_id': combo_a.combo_item_ids[0].id,
|
||||
}, {
|
||||
'order_id': self.cart.id,
|
||||
'product_id': product_b.id,
|
||||
'product_uom_qty': 3,
|
||||
'linked_line_id': combo_product_line.id,
|
||||
'combo_item_id': combo_b.combo_item_ids[0].id,
|
||||
},
|
||||
])
|
||||
|
||||
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
|
||||
self.assertEqual(combo_product_line._get_max_available_qty(), 2)
|
||||
self.assertEqual(combo_product_line._get_max_line_qty(), 5)
|
||||
self.assertEqual(combo_item_line_a._get_max_available_qty(), 2)
|
||||
self.assertEqual(combo_item_line_a._get_max_line_qty(), 5)
|
||||
|
||||
def test_get_max_line_qty_without_max(self):
|
||||
product = self._create_product(is_storable=True, allow_out_of_stock_order=True)
|
||||
combo = self.env['product.combo'].create({
|
||||
'name': "Test combo", 'combo_item_ids': [Command.create({'product_id': product.id})]
|
||||
})
|
||||
combo_product = self._create_product(type='combo', combo_ids=[Command.link(combo.id)])
|
||||
combo_product_line = self.env['sale.order.line'].create({
|
||||
'order_id': self.cart.id, 'product_id': combo_product.id, 'product_uom_qty': 3,
|
||||
})
|
||||
combo_item_line = self.env['sale.order.line'].create({
|
||||
'order_id': self.cart.id,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': 3,
|
||||
'linked_line_id': combo_product_line.id,
|
||||
})
|
||||
|
||||
self.assertIsNone(combo_product_line._get_max_available_qty())
|
||||
self.assertIsNone(combo_product_line._get_max_line_qty())
|
||||
self.assertIsNone(combo_item_line._get_max_available_qty())
|
||||
self.assertIsNone(combo_item_line._get_max_line_qty())
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import HttpCase, tagged
|
||||
|
||||
from odoo.addons.product.tests.common import ProductCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockProductConfigurator(ProductCommon, HttpCase):
|
||||
|
||||
def test_01_stock_message_update_after_close_with_optional_products(self):
|
||||
self.env['delivery.carrier'].search([]).is_published = False
|
||||
product_product_with_options = self.env['product.product'].create({
|
||||
'name': 'Product With Optional (TEST)',
|
||||
'standard_price': 500.0,
|
||||
'list_price': 750.0,
|
||||
'optional_product_ids': [Command.link(self.product.product_tmpl_id.id)],
|
||||
'website_published': True,
|
||||
'show_availability': True,
|
||||
'available_threshold': 5000,
|
||||
'allow_out_of_stock_order': False,
|
||||
'is_storable': True,
|
||||
})
|
||||
self.product.website_published = True
|
||||
self.env['stock.quant'].create({
|
||||
'product_id': product_product_with_options.id,
|
||||
'location_id': self.quick_ref('stock.stock_location_stock').id,
|
||||
'quantity': 30.0,
|
||||
})
|
||||
self.start_tour(
|
||||
"/",
|
||||
'website_sale_stock_message_after_close_onfigurator_modal_with_optional_products',
|
||||
)
|
||||
|
||||
def test_02_stock_message_update_after_close_without_optional_products(self):
|
||||
self.env['delivery.carrier'].search([]).is_published = False
|
||||
product_product_without_options = self.env['product.product'].create({
|
||||
'name': 'Product Without Optional (TEST)',
|
||||
'standard_price': 500.0,
|
||||
'list_price': 750.0,
|
||||
'website_published': True,
|
||||
'show_availability': True,
|
||||
'available_threshold': 5000,
|
||||
'allow_out_of_stock_order': False,
|
||||
'is_storable': True,
|
||||
})
|
||||
self.env['stock.quant'].create({
|
||||
'product_id': product_product_without_options.id,
|
||||
'location_id': self.quick_ref('stock.stock_location_stock').id,
|
||||
'quantity': 30.0,
|
||||
})
|
||||
self.start_tour(
|
||||
"/",
|
||||
'website_sale_stock_message_after_close_onfigurator_modal_without_optional_products',
|
||||
)
|
||||
|
|
@ -23,7 +23,7 @@ class TestStockNotificationProduct(HttpCase):
|
|||
cls.product = cls.env['product.product'].create({
|
||||
'name': 'Macbook Pro',
|
||||
'website_published': True,
|
||||
'type': 'product',
|
||||
'is_storable': True,
|
||||
'allow_out_of_stock_order': False,
|
||||
|
||||
})
|
||||
|
|
@ -35,8 +35,7 @@ class TestStockNotificationProduct(HttpCase):
|
|||
def test_back_in_stock_notification_product(self):
|
||||
self.start_tour("/", 'back_in_stock_notification_product')
|
||||
|
||||
partner_ids = self.env['res.partner']._mail_find_partner_from_emails(['test@test.test'])
|
||||
partner = partner_ids[0]
|
||||
partner = self.env['mail.thread']._partner_find_from_emails_single(['test@test.test'], no_create=True)
|
||||
ProductProduct = self.env['product.product']
|
||||
product = ProductProduct.browse(self.product.id)
|
||||
self.assertTrue(product._has_stock_notification(partner))
|
||||
|
|
@ -54,7 +53,10 @@ class TestStockNotificationProduct(HttpCase):
|
|||
})
|
||||
quants.action_apply_inventory()
|
||||
|
||||
website = self.env['website'].get_current_website()
|
||||
|
||||
ProductProduct._send_availability_email()
|
||||
emails = self.env['mail.mail'].search([('email_to', '=', partner.email_formatted)])
|
||||
self.assertEqual(emails[0].subject, "The product 'Macbook Pro' is now available")
|
||||
self.assertEqual(emails[0].email_from, website.company_id.partner_id.email_formatted)
|
||||
self.assertFalse(product._has_stock_notification(partner))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue