fix: group Odoo products by template — show variant count instead of duplicates
Search endpoint now queries product.template instead of product.product, so a product with 20 variants shows as 1 row with a "20 variants" badge instead of 20 duplicate rows. Category name shown in sub-text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -68,21 +68,52 @@ class WooProductSearchController(http.Controller):
|
||||
if instance.exists() and instance.excluded_category_ids:
|
||||
domain.append(('categ_id', 'not in', instance.excluded_category_ids.ids))
|
||||
|
||||
total = request.env['product.product'].search_count(domain)
|
||||
products = request.env['product.product'].search(domain, limit=limit, offset=offset)
|
||||
# Search product.template to group variants together
|
||||
tmpl_domain = []
|
||||
if query:
|
||||
tmpl_domain = [
|
||||
'|',
|
||||
('name', 'ilike', query),
|
||||
('default_code', 'ilike', query),
|
||||
]
|
||||
if category_id:
|
||||
tmpl_domain.append(('categ_id', '=', int(category_id)))
|
||||
if exclude_category_ids:
|
||||
if isinstance(exclude_category_ids, str):
|
||||
import json as _json2
|
||||
try:
|
||||
exclude_category_ids = _json2.loads(exclude_category_ids)
|
||||
except (ValueError, TypeError):
|
||||
exclude_category_ids = []
|
||||
if exclude_category_ids:
|
||||
tmpl_domain.append(('categ_id', 'not in', [int(x) for x in exclude_category_ids]))
|
||||
if apply_excluded and instance_id:
|
||||
instance = request.env['woo.instance'].browse(int(instance_id))
|
||||
if instance.exists() and instance.excluded_category_ids:
|
||||
tmpl_domain.append(('categ_id', 'not in', instance.excluded_category_ids.ids))
|
||||
|
||||
total = request.env['product.template'].search_count(tmpl_domain)
|
||||
templates = request.env['product.template'].search(tmpl_domain, limit=limit, offset=offset)
|
||||
|
||||
results = []
|
||||
for tmpl in templates:
|
||||
variant_count = len(tmpl.product_variant_ids)
|
||||
# Use first variant as representative
|
||||
first_variant = tmpl.product_variant_ids[:1]
|
||||
results.append({
|
||||
'id': first_variant.id if first_variant else tmpl.id,
|
||||
'template_id': tmpl.id,
|
||||
'name': tmpl.name,
|
||||
'default_code': tmpl.default_code or '',
|
||||
'list_price': tmpl.list_price,
|
||||
'qty_available': sum(tmpl.product_variant_ids.mapped('qty_available')),
|
||||
'categ_name': tmpl.categ_id.name if tmpl.categ_id else '',
|
||||
'variant_count': variant_count,
|
||||
'has_variants': variant_count > 1,
|
||||
})
|
||||
|
||||
return {
|
||||
'results': [
|
||||
{
|
||||
'id': p.id,
|
||||
'name': p.name,
|
||||
'default_code': p.default_code or '',
|
||||
'list_price': p.list_price,
|
||||
'qty_available': p.qty_available,
|
||||
'categ_name': p.categ_id.name if p.categ_id else '',
|
||||
}
|
||||
for p in products
|
||||
],
|
||||
'results': results,
|
||||
'total': total,
|
||||
}
|
||||
|
||||
|
||||
@@ -368,10 +368,18 @@
|
||||
<div class="woo-split-item"
|
||||
t-att-class="state.selectedOdooId === op.id ? 'selected' : ''"
|
||||
t-on-click="() => this.selectOdoo(op.id)">
|
||||
<div class="woo-split-item-name"><t t-esc="op.name"/></div>
|
||||
<div class="woo-split-item-name">
|
||||
<t t-esc="op.name"/>
|
||||
<t t-if="op.has_variants">
|
||||
<span class="woo-badge woo-badge-unmapped ms-2">
|
||||
<t t-esc="op.variant_count"/> variants
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="woo-split-item-sub">
|
||||
<t t-if="op.default_code">SKU: <t t-esc="op.default_code"/> · </t>
|
||||
$<t t-esc="op.list_price.toFixed(2)"/>
|
||||
<t t-esc="this.formatPrice(op.list_price)"/>
|
||||
<t t-if="op.categ_name"> · <t t-esc="op.categ_name"/></t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user