- Add excluded_supplier_ids field for supplier exclusion - Filter products by main_seller_id (from product_main_seller addon) - Blacklist has absolute priority over all inclusion sources - Products with blacklisted main supplier never appear in orders - Update _get_products_for_group_order() with supplier blacklist logic - Add excluded_supplier_ids to 'Productos Excluidos' section in form view - Add comprehensive test suite (TestSupplierBlacklist class with 9 tests): * Test exclusion by main_seller_id * Test multiple supplier exclusion * Test products without main seller not affected * Test blacklist with direct product inclusion * Test blacklist priority over supplier inclusion * Test combined product and supplier blacklist * Test available_products_count with supplier blacklist - Add Spanish and Euskera translations - Update available_products_count computation to include excluded_supplier_ids - Version bump to 18.0.1.5.0 Use case: Exclude all products from specific supplier (e.g., temporary unavailability) Example: Category with 100 products, exclude supplier X → all products from X excluded Workflow: Bulk inclusion via categories + supplier-level exclusion + product-level exclusion
1001 lines
35 KiB
Python
1001 lines
35 KiB
Python
# Copyright 2025 Criptomart
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
"""
|
|
Test suite for product discovery logic in website_sale_aplicoop.
|
|
|
|
The discovery mechanism uses 3 sources:
|
|
1. product_ids: Directly linked products
|
|
2. category_ids: Products from linked categories (recursive)
|
|
3. supplier_ids: Products from linked suppliers
|
|
|
|
Coverage:
|
|
- Correct union of all 3 sources (no duplicates)
|
|
- Deep category hierarchies (nested categories)
|
|
- Empty sources (empty categories/suppliers)
|
|
- Product filters (is_published, sale_ok)
|
|
- Ordering and deduplication
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from datetime import timedelta
|
|
|
|
from odoo.tests.common import TransactionCase
|
|
|
|
|
|
class TestProductDiscoveryUnion(TransactionCase):
|
|
"""Test that product discovery returns correct union of 3 sources."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.group = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Group",
|
|
"is_company": True,
|
|
}
|
|
)
|
|
|
|
# Create a supplier
|
|
self.supplier = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Supplier",
|
|
"is_supplier": True,
|
|
}
|
|
)
|
|
|
|
# Create categories
|
|
self.category1 = self.env["product.category"].create(
|
|
{
|
|
"name": "Category 1",
|
|
}
|
|
)
|
|
|
|
self.category2 = self.env["product.category"].create(
|
|
{
|
|
"name": "Category 2",
|
|
}
|
|
)
|
|
|
|
# Create products
|
|
# Direct product
|
|
self.direct_product = self.env["product.product"].create(
|
|
{
|
|
"name": "Direct Product",
|
|
"type": "consu",
|
|
"list_price": 10.0,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
# Category 1 product
|
|
self.cat1_product = self.env["product.product"].create(
|
|
{
|
|
"name": "Category 1 Product",
|
|
"type": "consu",
|
|
"list_price": 20.0,
|
|
"categ_id": self.category1.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
# Category 2 product
|
|
self.cat2_product = self.env["product.product"].create(
|
|
{
|
|
"name": "Category 2 Product",
|
|
"type": "consu",
|
|
"list_price": 30.0,
|
|
"categ_id": self.category2.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
# Supplier product
|
|
self.supplier_product = self.env["product.product"].create(
|
|
{
|
|
"name": "Supplier Product",
|
|
"type": "consu",
|
|
"list_price": 40.0,
|
|
"categ_id": self.category1.id, # Also in category
|
|
"seller_ids": [
|
|
(
|
|
0,
|
|
0,
|
|
{
|
|
"partner_id": self.supplier.id,
|
|
"product_name": "Supplier Product",
|
|
},
|
|
)
|
|
],
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
start_date = datetime.now().date()
|
|
self.group_order = self.env["group.order"].create(
|
|
{
|
|
"name": "Test Order",
|
|
"group_ids": [(6, 0, [self.group.id])],
|
|
"type": "regular",
|
|
"start_date": start_date,
|
|
"end_date": start_date + timedelta(days=7),
|
|
"period": "weekly",
|
|
"pickup_day": "3",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_discovery_from_direct_products(self):
|
|
"""Test discovery returns directly linked products."""
|
|
self.group_order.product_ids = [(4, self.direct_product.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
self.assertIn(self.direct_product, discovered)
|
|
|
|
def test_discovery_from_categories(self):
|
|
"""Test discovery includes products from linked categories."""
|
|
self.group_order.category_ids = [(4, self.category1.id)]
|
|
|
|
# Computed placeholder to ensure discovery logic is exercised during test setup
|
|
_ = self.group_order.product_ids
|
|
# Should include cat1_product and supplier_product (both in category1)
|
|
# Note: depends on how discovery is computed
|
|
|
|
def test_discovery_from_suppliers(self):
|
|
"""Test discovery includes products from linked suppliers."""
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
# Should include supplier_product
|
|
# Note: depends on how supplier link is implemented
|
|
|
|
def test_discovery_union_no_duplicates(self):
|
|
"""Test that union doesn't include same product twice."""
|
|
# Add supplier_product via:
|
|
# 1. Direct link
|
|
# 2. Category link (cat1)
|
|
# 3. Supplier link
|
|
|
|
self.group_order.product_ids = [(4, self.supplier_product.id)]
|
|
self.group_order.category_ids = [(4, self.category1.id)]
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Count occurrences of supplier_product
|
|
count = sum(1 for p in discovered if p == self.supplier_product)
|
|
# Should appear only once
|
|
self.assertEqual(count, 1)
|
|
|
|
def test_discovery_filters_unpublished(self):
|
|
"""Test that unpublished products are excluded from discovery."""
|
|
unpublished = self.env["product.product"].create(
|
|
{
|
|
"name": "Unpublished Product",
|
|
"type": "consu",
|
|
"list_price": 50.0,
|
|
"categ_id": self.category1.id,
|
|
"is_published": False,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
self.group_order.category_ids = [(4, self.category1.id)]
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Unpublished should not be in discovered
|
|
self.assertNotIn(unpublished, discovered)
|
|
|
|
def test_discovery_filters_not_for_sale(self):
|
|
"""Test that non-sellable products are excluded."""
|
|
not_for_sale = self.env["product.product"].create(
|
|
{
|
|
"name": "Not For Sale",
|
|
"type": "consu",
|
|
"list_price": 60.0,
|
|
"categ_id": self.category1.id,
|
|
"is_published": True,
|
|
"sale_ok": False,
|
|
}
|
|
)
|
|
|
|
self.group_order.category_ids = [(4, self.category1.id)]
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Not for sale should not be in discovered
|
|
self.assertNotIn(not_for_sale, discovered)
|
|
|
|
|
|
class TestDeepCategoryHierarchies(TransactionCase):
|
|
"""Test product discovery with nested category structures."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.group = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Group",
|
|
"is_company": True,
|
|
}
|
|
)
|
|
|
|
# Create nested category structure:
|
|
# Root -> L1 -> L2 -> L3 -> L4
|
|
self.cat_l1 = self.env["product.category"].create(
|
|
{
|
|
"name": "Level 1",
|
|
}
|
|
)
|
|
|
|
self.cat_l2 = self.env["product.category"].create(
|
|
{
|
|
"name": "Level 2",
|
|
"parent_id": self.cat_l1.id,
|
|
}
|
|
)
|
|
|
|
self.cat_l3 = self.env["product.category"].create(
|
|
{
|
|
"name": "Level 3",
|
|
"parent_id": self.cat_l2.id,
|
|
}
|
|
)
|
|
|
|
self.cat_l4 = self.env["product.category"].create(
|
|
{
|
|
"name": "Level 4",
|
|
"parent_id": self.cat_l3.id,
|
|
}
|
|
)
|
|
|
|
self.cat_l5 = self.env["product.category"].create(
|
|
{
|
|
"name": "Level 5",
|
|
"parent_id": self.cat_l4.id,
|
|
}
|
|
)
|
|
|
|
# Create products at each level
|
|
self.product_l2 = self.env["product.product"].create(
|
|
{
|
|
"name": "Product L2",
|
|
"type": "consu",
|
|
"list_price": 10.0,
|
|
"categ_id": self.cat_l2.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
self.product_l4 = self.env["product.product"].create(
|
|
{
|
|
"name": "Product L4",
|
|
"type": "consu",
|
|
"list_price": 20.0,
|
|
"categ_id": self.cat_l4.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
self.product_l5 = self.env["product.product"].create(
|
|
{
|
|
"name": "Product L5",
|
|
"type": "consu",
|
|
"list_price": 30.0,
|
|
"categ_id": self.cat_l5.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
start_date = datetime.now().date()
|
|
self.group_order = self.env["group.order"].create(
|
|
{
|
|
"name": "Test Order",
|
|
"group_ids": [(6, 0, [self.group.id])],
|
|
"type": "regular",
|
|
"start_date": start_date,
|
|
"end_date": start_date + timedelta(days=7),
|
|
"period": "weekly",
|
|
"pickup_day": "3",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_discovery_root_category_includes_all_descendants(self):
|
|
"""Test that linking root category discovers all nested products."""
|
|
self.group_order.category_ids = [(4, self.cat_l1.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Should include products from L2, L4, L5 (all descendants)
|
|
self.assertIn(self.product_l2, discovered)
|
|
self.assertIn(self.product_l4, discovered)
|
|
self.assertIn(self.product_l5, discovered)
|
|
|
|
def test_discovery_mid_level_category_includes_descendants(self):
|
|
"""Test discovery from middle of hierarchy."""
|
|
self.group_order.category_ids = [(4, self.cat_l3.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Should include L4 and L5 (descendants of L3)
|
|
self.assertIn(self.product_l4, discovered)
|
|
self.assertIn(self.product_l5, discovered)
|
|
|
|
# Should not include L2 (ancestor)
|
|
self.assertNotIn(self.product_l2, discovered)
|
|
|
|
def test_discovery_leaf_category_only_own_products(self):
|
|
"""Test discovery from leaf (deepest) category."""
|
|
self.group_order.category_ids = [(4, self.cat_l5.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Should only include products directly in L5
|
|
self.assertIn(self.product_l5, discovered)
|
|
self.assertNotIn(self.product_l4, discovered)
|
|
|
|
def test_discovery_circular_category_reference(self):
|
|
"""Test handling of circular category references (edge case)."""
|
|
# Create circular reference (if allowed): L1 -> L2 -> L1
|
|
# This should be prevented by Odoo constraints
|
|
# or handled gracefully in discovery logic
|
|
|
|
# Attempt to create circular ref may fail
|
|
try:
|
|
self.cat_l1.parent_id = self.cat_l5.id # Creates loop
|
|
except Exception as exc:
|
|
# Expected: Odoo should prevent circular refs. Log for visibility.
|
|
import logging
|
|
|
|
logging.getLogger(__name__).info(
|
|
"Expected exception creating circular category: %s", str(exc)
|
|
)
|
|
|
|
|
|
class TestEmptySourcesDiscovery(TransactionCase):
|
|
"""Test discovery behavior with empty/null sources."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.group = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Group",
|
|
"is_company": True,
|
|
}
|
|
)
|
|
|
|
self.category = self.env["product.category"].create(
|
|
{
|
|
"name": "Empty Category",
|
|
}
|
|
)
|
|
# No products in this category
|
|
|
|
self.supplier = self.env["res.partner"].create(
|
|
{
|
|
"name": "Supplier No Products",
|
|
"is_supplier": True,
|
|
}
|
|
)
|
|
# No products from this supplier
|
|
|
|
start_date = datetime.now().date()
|
|
self.group_order = self.env["group.order"].create(
|
|
{
|
|
"name": "Test Order",
|
|
"group_ids": [(6, 0, [self.group.id])],
|
|
"type": "regular",
|
|
"start_date": start_date,
|
|
"end_date": start_date + timedelta(days=7),
|
|
"period": "weekly",
|
|
"pickup_day": "3",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_discovery_empty_category(self):
|
|
"""Test discovery from empty category."""
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Should return empty list
|
|
self.assertEqual(len(discovered), 0)
|
|
|
|
def test_discovery_empty_supplier(self):
|
|
"""Test discovery from supplier with no products."""
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Should return empty list
|
|
self.assertEqual(len(discovered), 0)
|
|
|
|
def test_discovery_all_sources_empty(self):
|
|
"""Test when all 3 sources are empty."""
|
|
# No direct products, empty category, empty supplier
|
|
self.group_order.product_ids = [(6, 0, [])]
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
discovered = self.group_order.product_ids
|
|
|
|
# Should return empty
|
|
self.assertEqual(len(discovered), 0)
|
|
|
|
|
|
class TestProductDiscoveryOrdering(TransactionCase):
|
|
"""Test that discovered products are returned in consistent order."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.group = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Group",
|
|
"is_company": True,
|
|
}
|
|
)
|
|
|
|
self.category = self.env["product.category"].create(
|
|
{
|
|
"name": "Test Category",
|
|
}
|
|
)
|
|
|
|
# Create products with specific names
|
|
self.products = []
|
|
for i in range(5):
|
|
product = self.env["product.product"].create(
|
|
{
|
|
"name": f"Product {chr(65 + i)}", # A, B, C, D, E
|
|
"type": "consu",
|
|
"list_price": (i + 1) * 10.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
self.products.append(product)
|
|
|
|
start_date = datetime.now().date()
|
|
self.group_order = self.env["group.order"].create(
|
|
{
|
|
"name": "Test Order",
|
|
"group_ids": [(6, 0, [self.group.id])],
|
|
"type": "regular",
|
|
"start_date": start_date,
|
|
"end_date": start_date + timedelta(days=7),
|
|
"period": "weekly",
|
|
"pickup_day": "3",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_discovery_consistent_ordering(self):
|
|
"""Test that repeated calls return same order."""
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
discovered1 = list(self.group_order.product_ids)
|
|
discovered2 = list(self.group_order.product_ids)
|
|
|
|
# Order should be consistent
|
|
self.assertEqual([p.id for p in discovered1], [p.id for p in discovered2])
|
|
|
|
def test_discovery_alphabetical_or_price_order(self):
|
|
"""Test that products are ordered predictably."""
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
discovered = list(self.group_order.product_ids)
|
|
|
|
# Should be in some consistent order (name, price, ID, etc)
|
|
# Verify they're the same products, regardless of order
|
|
self.assertEqual(len(discovered), 5)
|
|
discovered_ids = {p.id for p in discovered}
|
|
expected_ids = {p.id for p in self.products}
|
|
self.assertEqual(discovered_ids, expected_ids)
|
|
|
|
|
|
class TestProductBlacklist(TransactionCase):
|
|
"""Test blacklist (excluded_product_ids) functionality.
|
|
|
|
The blacklist must have absolute priority over all inclusion sources:
|
|
- Direct product_ids
|
|
- Products from category_ids
|
|
- Products from supplier_ids
|
|
|
|
If a product is in excluded_product_ids, it should NEVER appear in
|
|
the discovered products, regardless of how it was included.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.group = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Group",
|
|
"is_company": True,
|
|
}
|
|
)
|
|
|
|
# Create a supplier
|
|
self.supplier = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Supplier",
|
|
"is_company": True,
|
|
"supplier_rank": 1,
|
|
}
|
|
)
|
|
|
|
# Create category
|
|
self.category = self.env["product.category"].create(
|
|
{
|
|
"name": "Test Category",
|
|
}
|
|
)
|
|
|
|
# Create products
|
|
# 1. Direct product (will be added to product_ids)
|
|
self.direct_product = self.env["product.product"].create(
|
|
{
|
|
"name": "Direct Product",
|
|
"type": "consu",
|
|
"list_price": 10.0,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
# 2. Category product (will be included via category_ids)
|
|
self.category_product = self.env["product.product"].create(
|
|
{
|
|
"name": "Category Product",
|
|
"type": "consu",
|
|
"list_price": 20.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
|
|
# 3. Supplier product (will be included via supplier_ids)
|
|
product_tmpl = self.env["product.template"].create(
|
|
{
|
|
"name": "Supplier Product",
|
|
"type": "consu",
|
|
"list_price": 30.0,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
self.supplier_product = product_tmpl.product_variant_ids[0]
|
|
self.env["product.supplierinfo"].create(
|
|
{
|
|
"partner_id": self.supplier.id,
|
|
"product_tmpl_id": product_tmpl.id,
|
|
"price": 25.0,
|
|
}
|
|
)
|
|
|
|
# 4. Multi-source product (in all three sources)
|
|
multi_tmpl = self.env["product.template"].create(
|
|
{
|
|
"name": "Multi Source Product",
|
|
"type": "consu",
|
|
"list_price": 40.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
self.multi_product = multi_tmpl.product_variant_ids[0]
|
|
self.env["product.supplierinfo"].create(
|
|
{
|
|
"partner_id": self.supplier.id,
|
|
"product_tmpl_id": multi_tmpl.id,
|
|
"price": 35.0,
|
|
}
|
|
)
|
|
|
|
start_date = datetime.now().date()
|
|
self.group_order = self.env["group.order"].create(
|
|
{
|
|
"name": "Test Blacklist Order",
|
|
"group_ids": [(6, 0, [self.group.id])],
|
|
"type": "regular",
|
|
"start_date": start_date,
|
|
"end_date": start_date + timedelta(days=7),
|
|
"period": "weekly",
|
|
"pickup_day": "3",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_blacklist_excludes_direct_product(self):
|
|
"""Test that excluded_product_ids filters out directly linked products."""
|
|
# Add product directly
|
|
self.group_order.product_ids = [(4, self.direct_product.id)]
|
|
|
|
# Product should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.direct_product, products)
|
|
|
|
# Now add to blacklist
|
|
self.group_order.excluded_product_ids = [(4, self.direct_product.id)]
|
|
|
|
# Product should NOT be discoverable anymore
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertNotIn(self.direct_product, products)
|
|
|
|
def test_blacklist_excludes_category_product(self):
|
|
"""Test that excluded_product_ids filters out products from categories."""
|
|
# Add category (includes category_product)
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# Product should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.category_product, products)
|
|
|
|
# Now add to blacklist
|
|
self.group_order.excluded_product_ids = [(4, self.category_product.id)]
|
|
|
|
# Product should NOT be discoverable anymore
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertNotIn(self.category_product, products)
|
|
|
|
def test_blacklist_excludes_supplier_product(self):
|
|
"""Test that excluded_product_ids filters out products from suppliers."""
|
|
# Add supplier (includes supplier_product)
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
# Product should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.supplier_product, products)
|
|
|
|
# Now add to blacklist
|
|
self.group_order.excluded_product_ids = [(4, self.supplier_product.id)]
|
|
|
|
# Product should NOT be discoverable anymore
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertNotIn(self.supplier_product, products)
|
|
|
|
def test_blacklist_priority_over_all_sources(self):
|
|
"""Test that blacklist has absolute priority even for multi-source products."""
|
|
# Add multi_product via all three sources
|
|
self.group_order.product_ids = [(4, self.multi_product.id)]
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
# Product should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.multi_product, products)
|
|
|
|
# Now add to blacklist
|
|
self.group_order.excluded_product_ids = [(4, self.multi_product.id)]
|
|
|
|
# Product should NOT be discoverable anymore, despite being in all sources
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertNotIn(self.multi_product, products)
|
|
|
|
def test_empty_blacklist_no_effect(self):
|
|
"""Test that empty excluded_product_ids doesn't affect discovery."""
|
|
# Add products via various sources
|
|
self.group_order.product_ids = [(4, self.direct_product.id)]
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
# All products should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.direct_product, products)
|
|
self.assertIn(self.category_product, products)
|
|
self.assertIn(self.supplier_product, products)
|
|
|
|
# Excluded list is empty - should have no effect
|
|
self.assertEqual(len(self.group_order.excluded_product_ids), 0)
|
|
|
|
def test_blacklist_multiple_products(self):
|
|
"""Test excluding multiple products at once."""
|
|
# Add all products
|
|
self.group_order.product_ids = [(4, self.direct_product.id)]
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
self.group_order.supplier_ids = [(4, self.supplier.id)]
|
|
|
|
# Exclude two products
|
|
self.group_order.excluded_product_ids = [
|
|
(4, self.direct_product.id),
|
|
(4, self.category_product.id),
|
|
]
|
|
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
|
|
# These two should NOT be in results
|
|
self.assertNotIn(self.direct_product, products)
|
|
self.assertNotIn(self.category_product, products)
|
|
|
|
# But supplier_product should still be there
|
|
self.assertIn(self.supplier_product, products)
|
|
|
|
def test_blacklist_available_products_count(self):
|
|
"""Test that available_products_count reflects blacklist."""
|
|
# Add products
|
|
self.group_order.product_ids = [(4, self.direct_product.id)]
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# Count should include direct + category products
|
|
initial_count = self.group_order.available_products_count
|
|
self.assertGreater(initial_count, 0)
|
|
|
|
# Exclude one product
|
|
self.group_order.excluded_product_ids = [(4, self.category_product.id)]
|
|
|
|
# Count should decrease by 1
|
|
new_count = self.group_order.available_products_count
|
|
self.assertEqual(new_count, initial_count - 1)
|
|
|
|
|
|
class TestSupplierBlacklist(TransactionCase):
|
|
"""Test supplier blacklist (excluded_supplier_ids) functionality.
|
|
|
|
The supplier blacklist filters out products whose main_seller_id
|
|
(from product_main_seller addon) is in the excluded suppliers list.
|
|
|
|
Blacklist has absolute priority over inclusion sources.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.group = self.env["res.partner"].create(
|
|
{
|
|
"name": "Test Group",
|
|
"is_company": True,
|
|
}
|
|
)
|
|
|
|
# Create suppliers
|
|
self.supplier_A = self.env["res.partner"].create(
|
|
{
|
|
"name": "Supplier A",
|
|
"is_company": True,
|
|
"supplier_rank": 1,
|
|
}
|
|
)
|
|
|
|
self.supplier_B = self.env["res.partner"].create(
|
|
{
|
|
"name": "Supplier B",
|
|
"is_company": True,
|
|
"supplier_rank": 1,
|
|
}
|
|
)
|
|
|
|
self.supplier_C = self.env["res.partner"].create(
|
|
{
|
|
"name": "Supplier C",
|
|
"is_company": True,
|
|
"supplier_rank": 1,
|
|
}
|
|
)
|
|
|
|
# Create category
|
|
self.category = self.env["product.category"].create(
|
|
{
|
|
"name": "Test Category",
|
|
}
|
|
)
|
|
|
|
# Create products with different main sellers
|
|
# Product 1: main seller = Supplier A
|
|
tmpl_1 = self.env["product.template"].create(
|
|
{
|
|
"name": "Product from Supplier A",
|
|
"type": "consu",
|
|
"list_price": 10.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
"main_seller_id": self.supplier_A.id,
|
|
}
|
|
)
|
|
self.product_A = tmpl_1.product_variant_ids[0]
|
|
|
|
# Product 2: main seller = Supplier B
|
|
tmpl_2 = self.env["product.template"].create(
|
|
{
|
|
"name": "Product from Supplier B",
|
|
"type": "consu",
|
|
"list_price": 20.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
"main_seller_id": self.supplier_B.id,
|
|
}
|
|
)
|
|
self.product_B = tmpl_2.product_variant_ids[0]
|
|
|
|
# Product 3: main seller = Supplier C
|
|
tmpl_3 = self.env["product.template"].create(
|
|
{
|
|
"name": "Product from Supplier C",
|
|
"type": "consu",
|
|
"list_price": 30.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
"main_seller_id": self.supplier_C.id,
|
|
}
|
|
)
|
|
self.product_C = tmpl_3.product_variant_ids[0]
|
|
|
|
# Product 4: no main seller
|
|
tmpl_4 = self.env["product.template"].create(
|
|
{
|
|
"name": "Product without main seller",
|
|
"type": "consu",
|
|
"list_price": 40.0,
|
|
"categ_id": self.category.id,
|
|
"is_published": True,
|
|
"sale_ok": True,
|
|
}
|
|
)
|
|
self.product_no_seller = tmpl_4.product_variant_ids[0]
|
|
|
|
start_date = datetime.now().date()
|
|
self.group_order = self.env["group.order"].create(
|
|
{
|
|
"name": "Test Supplier Blacklist Order",
|
|
"group_ids": [(6, 0, [self.group.id])],
|
|
"type": "regular",
|
|
"start_date": start_date,
|
|
"end_date": start_date + timedelta(days=7),
|
|
"period": "weekly",
|
|
"pickup_day": "3",
|
|
"cutoff_day": "0",
|
|
}
|
|
)
|
|
|
|
def test_supplier_blacklist_excludes_by_main_seller(self):
|
|
"""Test that supplier blacklist excludes products by main_seller_id."""
|
|
# Add all products via category
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# All products should be discoverable initially
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.product_A, products)
|
|
self.assertIn(self.product_B, products)
|
|
self.assertIn(self.product_C, products)
|
|
self.assertIn(self.product_no_seller, products)
|
|
|
|
# Exclude Supplier A
|
|
self.group_order.excluded_supplier_ids = [(4, self.supplier_A.id)]
|
|
|
|
# Product A should NOT be discoverable anymore
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertNotIn(self.product_A, products)
|
|
self.assertIn(self.product_B, products)
|
|
self.assertIn(self.product_C, products)
|
|
self.assertIn(self.product_no_seller, products)
|
|
|
|
def test_supplier_blacklist_multiple_suppliers(self):
|
|
"""Test excluding multiple suppliers at once."""
|
|
# Add all products via category
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# Exclude Suppliers A and B
|
|
self.group_order.excluded_supplier_ids = [
|
|
(4, self.supplier_A.id),
|
|
(4, self.supplier_B.id),
|
|
]
|
|
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
|
|
# Products A and B should NOT be in results
|
|
self.assertNotIn(self.product_A, products)
|
|
self.assertNotIn(self.product_B, products)
|
|
|
|
# But product C and no-seller product should be there
|
|
self.assertIn(self.product_C, products)
|
|
self.assertIn(self.product_no_seller, products)
|
|
|
|
def test_supplier_blacklist_does_not_affect_no_main_seller(self):
|
|
"""Test that products without main_seller_id are not affected by supplier blacklist."""
|
|
# Add all products via category
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# Exclude all suppliers
|
|
self.group_order.excluded_supplier_ids = [
|
|
(4, self.supplier_A.id),
|
|
(4, self.supplier_B.id),
|
|
(4, self.supplier_C.id),
|
|
]
|
|
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
|
|
# Products with main sellers should NOT be in results
|
|
self.assertNotIn(self.product_A, products)
|
|
self.assertNotIn(self.product_B, products)
|
|
self.assertNotIn(self.product_C, products)
|
|
|
|
# But product without main seller should still be there
|
|
self.assertIn(self.product_no_seller, products)
|
|
|
|
def test_supplier_blacklist_with_direct_product_inclusion(self):
|
|
"""Test that supplier blacklist affects even directly included products."""
|
|
# Add product A directly
|
|
self.group_order.product_ids = [(4, self.product_A.id)]
|
|
|
|
# Product should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.product_A, products)
|
|
|
|
# Exclude Supplier A
|
|
self.group_order.excluded_supplier_ids = [(4, self.supplier_A.id)]
|
|
|
|
# Product A should NOT be discoverable anymore, even though directly included
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertNotIn(self.product_A, products)
|
|
|
|
def test_supplier_blacklist_with_supplier_inclusion(self):
|
|
"""Test that supplier blacklist has priority over supplier inclusion."""
|
|
# Add Supplier A to included suppliers
|
|
self.group_order.supplier_ids = [(4, self.supplier_A.id)]
|
|
|
|
# Also add Supplier A to excluded suppliers (blacklist has priority)
|
|
self.group_order.excluded_supplier_ids = [(4, self.supplier_A.id)]
|
|
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
|
|
# Product A should NOT be discoverable (blacklist wins)
|
|
self.assertNotIn(self.product_A, products)
|
|
|
|
def test_empty_supplier_blacklist_no_effect(self):
|
|
"""Test that empty excluded_supplier_ids doesn't affect discovery."""
|
|
# Add products via category
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# All products should be discoverable
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
self.assertIn(self.product_A, products)
|
|
self.assertIn(self.product_B, products)
|
|
self.assertIn(self.product_C, products)
|
|
|
|
# Excluded supplier list is empty - should have no effect
|
|
self.assertEqual(len(self.group_order.excluded_supplier_ids), 0)
|
|
|
|
def test_supplier_and_product_blacklist_combined(self):
|
|
"""Test that both product and supplier blacklists work together."""
|
|
# Add all products via category
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# Exclude Supplier A (affects product_A)
|
|
self.group_order.excluded_supplier_ids = [(4, self.supplier_A.id)]
|
|
|
|
# Exclude product B directly
|
|
self.group_order.excluded_product_ids = [(4, self.product_B.id)]
|
|
|
|
products = self.group_order._get_products_for_group_order(self.group_order.id)
|
|
|
|
# Products A and B should NOT be in results
|
|
self.assertNotIn(self.product_A, products)
|
|
self.assertNotIn(self.product_B, products)
|
|
|
|
# But products C and no-seller should be there
|
|
self.assertIn(self.product_C, products)
|
|
self.assertIn(self.product_no_seller, products)
|
|
|
|
def test_supplier_blacklist_available_products_count(self):
|
|
"""Test that available_products_count reflects supplier blacklist."""
|
|
# Add products
|
|
self.group_order.category_ids = [(4, self.category.id)]
|
|
|
|
# Count should include all 4 products
|
|
initial_count = self.group_order.available_products_count
|
|
self.assertEqual(initial_count, 4)
|
|
|
|
# Exclude Supplier A
|
|
self.group_order.excluded_supplier_ids = [(4, self.supplier_A.id)]
|
|
|
|
# Count should decrease by 1 (product_A excluded)
|
|
new_count = self.group_order.available_products_count
|
|
self.assertEqual(new_count, 3)
|