mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 23:12:00 +02:00
Initial commit: Sale packages
This commit is contained in:
commit
14e3d26998
6469 changed files with 2479670 additions and 0 deletions
|
|
@ -0,0 +1,8 @@
|
|||
# -*- 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_multilang
|
||||
from . import test_website_sale_stock_product_warehouse
|
||||
from . import test_website_sale_stock_stock_notification
|
||||
from . import test_website_sale_stock_reorder_from_portal
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# -*- 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.addons.website_sale.tests.test_website_sale_cart_abandoned import TestWebsiteSaleCartAbandonedCommon
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockAbandonedCartEmail(TestWebsiteSaleCartAbandonedCommon):
|
||||
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
|
||||
|
||||
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
|
||||
order_line = [[0, 0, {
|
||||
'name': 'The Product',
|
||||
'product_id': storable_product_product.id,
|
||||
'product_uom_qty': 1,
|
||||
}]]
|
||||
customer = self.env['res.partner'].create({
|
||||
'name': 'a',
|
||||
'email': 'a@example.com',
|
||||
})
|
||||
sale_order = self.env['sale.order'].create({
|
||||
'partner_id': customer.id,
|
||||
'website_id': website.id,
|
||||
'state': 'draft',
|
||||
'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
|
||||
minutes=1),
|
||||
'order_line': order_line
|
||||
})
|
||||
|
||||
self.assertFalse(self.send_mail_patched(sale_order.id))
|
||||
# Reset cart_recovery sent state
|
||||
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.assertTrue(self.send_mail_patched(sale_order.id))
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# 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
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockMultilang(HttpCase):
|
||||
def test_website_sale_stock_multilang(self):
|
||||
# Install French
|
||||
website = self.env.ref('website.default_website')
|
||||
lang_fr = self.env['res.lang']._activate_lang('fr_FR')
|
||||
website.language_ids = [Command.link(lang_fr.id)]
|
||||
|
||||
# Configure product: out-of-stock message in EN and FR
|
||||
unavailable_product = self.env['product.product'].create({
|
||||
'name': 'unavailable_product',
|
||||
'type': 'product',
|
||||
'allow_out_of_stock_order': False,
|
||||
'sale_ok': True,
|
||||
'website_published': True,
|
||||
'list_price': 123.45,
|
||||
'out_of_stock_message': 'Out of stock',
|
||||
})
|
||||
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,106 @@
|
|||
# -*- 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
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockProductWarehouse(TestSaleProductAttributeValueCommon):
|
||||
|
||||
@classmethod
|
||||
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)
|
||||
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)
|
||||
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',
|
||||
})
|
||||
|
||||
# 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)]])
|
||||
|
||||
# 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()
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
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_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):
|
||||
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")
|
||||
|
||||
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)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteSaleStockReorderFromPortal(HttpCase):
|
||||
@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,
|
||||
})
|
||||
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, {
|
||||
'product_id': cls.available_product.id,
|
||||
'product_uom_qty': 1,
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': cls.unavailable_product.id,
|
||||
'product_uom_qty': 1,
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': cls.partially_available_product.id,
|
||||
'product_uom_qty': 2,
|
||||
})
|
||||
]
|
||||
})
|
||||
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()
|
||||
|
||||
def test_website_sale_stock_reorder_from_portal_stock(self):
|
||||
self.start_tour("/", 'website_sale_stock_reorder_from_portal', login='admin')
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestStockNotificationProduct(HttpCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
warehouse = cls.env['stock.warehouse'].create({
|
||||
'name': 'Wishlist Warehouse',
|
||||
'code': 'W_WH'
|
||||
})
|
||||
current_website = cls.env['website'].get_current_website()
|
||||
current_website.warehouse_id = warehouse
|
||||
|
||||
cls.warehouse = warehouse
|
||||
cls.current_website = current_website
|
||||
|
||||
cls.product = cls.env['product.product'].create({
|
||||
'name': 'Macbook Pro',
|
||||
'website_published': True,
|
||||
'type': 'product',
|
||||
'allow_out_of_stock_order': False,
|
||||
|
||||
})
|
||||
cls.pricelist = cls.env['product.pricelist'].create({
|
||||
'name': 'Public Pricelist',
|
||||
})
|
||||
cls.currency = cls.env.ref("base.USD")
|
||||
|
||||
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]
|
||||
ProductProduct = self.env['product.product']
|
||||
product = ProductProduct.browse(self.product.id)
|
||||
self.assertTrue(product._has_stock_notification(partner))
|
||||
|
||||
# No email should be sent
|
||||
ProductProduct._send_availability_email()
|
||||
emails = self.env['mail.mail'].search([('email_to', '=', partner.email_formatted)])
|
||||
self.assertEqual(len(emails), 0)
|
||||
|
||||
# Replenish Product
|
||||
quants = self.env['stock.quant'].with_context(inventory_mode=True).create({
|
||||
'product_id': self.product.id,
|
||||
'inventory_quantity': 10.0,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
})
|
||||
quants.action_apply_inventory()
|
||||
|
||||
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.assertFalse(product._has_stock_notification(partner))
|
||||
Loading…
Add table
Add a link
Reference in a new issue